如何将MetaData添加到动态构建的MVC3 ViewModel中?

时间:2012-09-14 14:32:30

标签: c# asp.net-mvc asp.net-mvc-3 modelmetadata modelmetadataprovider

我正在研究的项目的一个关键特性是用户能够根据预先存在的字段类型池(众所周知的类型)配置表单(如“表单”中的填充) ,例如“用户名”,“出生日期”等,还有“泛型”,如“字符串”,“日期时间”等。)。

我们曾经有一个静态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; }
}

这就像一个魅力。剃刀视图只需迭代集合,以超过标准的方式为集合的每个属性创建相应的控件:

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

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

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

为了提供这些MetaData,我创建了一个继承自CustomModelMetadataProvider的{​​{1}},并将其注册为Global.asax中的新DataAnnotationsModelMetadataProvider。创建ViewModel时会调用ModelMetadataProvider函数,而对于我的ViewModel的每个属性,都会调用{...}这样的函数。

问题出现的地方:为了向当前属性添加一些元数据,我首先需要确定我当前正在查看的属性(“名称”的最大长度为200,“日期”出生“并非如此,我无法为每个属性默认分配maxlength)。还有一些我没有设法做到这一点,因为所有属性都具有相同的名称CreateMetadata()和相同的容器类型Value

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

m => m.Properties

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

更新:在一段时间内向各个方向翻转后,我们终于走了另一条道路。我们基本上使用不引人注目的javascript来使用MVC的验证功能,而不涉及属性或元数据。简而言之,我们将var container = modelAccessor.Target.GetType().GetField("container"); var containerObject = (UserInputModel)container.GetValue(modelAccessor.Target); (以及所有其他必需属性)等HTML属性添加到value-data="true"语句中。这非常适用于所有原子验证(必需,stringlength等)。

2 个答案:

答案 0 :(得分:0)

Tim,您可以通过Ajax与您的属性Remote attribute利用看似客户端验证的内容。

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

这种方法的一个缺陷是你需要为你想要支持的每种类型和条件编写验证方法。不过,听起来你不得不走这条路。

希望这有帮助。

答案 1 :(得分:0)

看看这篇文章是否对您有所帮助:Technique for carrying metadata to View Models with AutoMapper

也可以将此用于创意(自定义模型元数据提供程序):changing viewmodel's MetadataType attribute at runtime

在我看来,

Fluent validation可能是你最好的选择,但显然由你来选择上述最佳匹配。

<强>更新

尝试使用ModelMetadata并覆盖ModelMetadataProviderDive Deep Into MVC: ModelMetadata and ModelMetadataProvider。这样,您可以完全自定义模型元数据(这将替换数据注释),并且您可以完全控制正在发生的事情,而不是依赖于ASP.NET MVC。

另一个值得关注的好地方是Creating your own ModelMetadataProvider to handle custom attributes

希望这对你有所帮助。