1

我正在处理的一个项目的主要功能之一是用户能够基于预先存在的字段类型(例如众所周知的类型)配置表单(如在“表单”中填写) “用户名”、“出生日期”等,还有“通用类型”,如“字符串”、“日期时间”等)。

我们曾经有一个静态 ViewModel,它适用于“众所周知的”类型,看起来像这样:

public class UserInputModel
{
    [StringLength(200)]
    public string Name { get; set; }

    [Required(ErrorMessageResourceName = "BirthDateEmptyError", ErrorMessageResourceType = typeof(Resources.ErrorMessages))]
    public DateTime BirthDate { get; set; }

    //Here comes a lot of other properties
}

列出了所有已知的属性,我们根据上下文显示或隐藏它们。

但是最后一个要求来了,改变了这一切。用户现在可以根据需要添加任意数量的泛型类型字段。为了做到这一点,我们决定让这个 InputModel 完全动态化。现在看起来像这样:

public class UserInputModel
{
    // Each ModelProperty has an "Id" and a "Value" property
    public ICollection<ModelProperty> Properties { get; set; }
}

这就像一个魅力。razor 视图只需要遍历集合,为集合的每个属性创建相应的控件,而不是标准的方式:

@Html.TextBoxFor(m => m.Properties[index].Value);

...我们很好地将数据作为填充表格返回。

=> 这很好用,但我们没有任何客户端验证。为此,我们需要一些元数据......我们不再通过注释获得,因为我们正在动态创建模型。

为了提供这些元数据,我创建了一个CustomModelMetadataProvider继承自Global.asaxDataAnnotationsModelMetadataProvider并将其注册为新的。ModelMetadataProviderCreateMetadata()函数在创建 ViewModel 时被调用,并且对于我的 ViewModel 的每个属性......到目前为止都很好。

问题从哪里开始:为了向当前属性添加一些元数据,我首先需要确定我当前正在查看的属性(“名称”的最大长度为 200,“出生日期”没有,所以我无法分配默认情况下每个属性的最大长度)。不知何故,我还没有设法做到这一点,因为所有属性都具有相同的名称Value和相同的容器类型ModelProperty

我尝试通过反射访问属性的容器,但由于 ModelAccessor 的目标是 ViewModel 本身(因为 lambda 表达式m => m.Properties),以下构造为我提供了整个 ViewModel,而不仅仅是 ModelProperty:

var container = modelAccessor.Target.GetType().GetField("container");
var containerObject = (UserInputModel)container.GetValue(modelAccessor.Target);

我一直在翻来覆去,但找不到一种方法来识别我手头的 ModelProperty。有没有办法做到这一点?

更新:在所有可能的方向翻转了一段时间后,我们终于走了另一条路。我们基本上使用不显眼的 javascript 来使用 MVC 的验证功能,而无需触及属性或元数据。简而言之,我们将 HTML 属性value-data="true"(以及所有其他必需的属性)添加到@Html.TextBoxFor()语句中。这对于所有原子验证(必需、字符串长度等)都非常有效。

4

2 回答 2

0

看看这篇文章是否对您有帮助:使用 AutoMapper 将元数据传输到视图模型的技术

也可以将这个用于想法(自定义模型元数据提供程序):在运行时更改视图模型的 MetadataType 属性

在我看来, Fluent 验证可能是您的最佳选择,但显然您可以在上述选项中选择最佳匹配。

更新

尝试使用ModelMetadata并覆盖ModelMetadataProvider深入了解 MVC:ModelMetadata 和 ModelMetadataProvider。通过这种方式,您可以完全自定义模型元数据(这取代了数据注释),并且您可以完全控制正在发生的事情,而不是依赖 ASP.NET MVC。

另一个值得关注的地方是创建您自己的 ModelMetadataProvider 来处理自定义属性

希望这一切对你有所帮助。

于 2012-09-14T15:35:16.527 回答
0

Tim,您可以利用属性上的Remote 属性通过 Ajax 进行客户端验证。

基本上,您需要设置一个验证控制器,然后将一些智能写入该控制器。但至少您可以编写一些辅助方法并将它们全部保存在一个地方。根据您向最终用户提供的元数据,您将拥有一系列验证器,并且每个验证器方法都适用于具有良好重用性的特定类型。

这种方法的一个缺陷是您需要为要支持的每种类型和条件编写验证方法。不过,听起来你无论如何都必须走那条路。

希望这可以帮助。

于 2012-09-14T14:47:35.773 回答