使用具有MVC DataAnnotations和MetaDataType的多个接口

时间:2011-10-19 05:13:17

标签: model-view-controller data-annotations

我正在使用DataAnnotations将验证应用于MVC ViewModel,它是几个实体框架对象和一些自定义逻辑的组合。已经为接口中的实体对象定义了验证,但是如何将此验证应用于ViewModel?

我最初的想法是将接口合并为一个并将组合接口应用于ViewModel,但这不起作用。这是一些示例代码,展示了我的意思:

// interfaces containing DataAnnotations implemented by entity framework classes
public interface IPerson
{
    [Required]
    [Display(Name = "First Name")]
    string FirstName { get; set; }

    [Required]
    [Display(Name = "Last Name")]
    string LastName { get; set; }

    [Required]
    int Age { get; set; }
}
public interface IAddress
{
    [Required]
    [Display(Name = "Street")]
    string Street1 { get; set; }

    [Display(Name = "")]
    string Street2 { get; set; }

    [Required]
    string City { get; set; }

    [Required]
    string State { get; set; }

    [Required]
    string Country { get; set; }
}

// partial entity framework classes to specify interfaces
public partial class Person : IPerson {}
public partial class Address : IAddress {}

// combined interface
public interface IPersonViewModel : IPerson, IAddress {}

// ViewModel flattening a Person with Address for use in View
[MetadataType(typeof(IPersonViewModel))] // <--- This does not work. 
public class PersonViewModel : IPersonViewModel
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int Age { get; set; }

    public string Street1 { get; set; }

    public string Street2 { get; set; }

    public string City { get; set; }

    public string State { get; set; }

    public string Country { get; set; }
}

我的真实世界问题涉及ViewModel上的大约150个属性,因此它不像样本那样微不足道,重新输入所有属性似乎是对DRY的可怕违反。

关于如何实现这一目标的任何想法?

3 个答案:

答案 0 :(得分:7)

为了使其工作,您需要手动将接口关联为具体类的元数据。

我希望能够添加多个MetadataType属性,但这是不允许的。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] // Notice AllowMultiple
public sealed class MetadataTypeAttribute : Attribute

因此,这会产生编译错误:

[MetadataType(typeof(IPerson))] 
[MetadataType(typeof(IAddress))] // <--- Duplicate 'MetadataType' attribute 
public class PersonViewModel : IPersonViewModel

但是,只有一个界面才有效。所以我的解决方案是简单地使用AssociatedMetadataTypeTypeDescriptionProvider关联接口并将其包装在另一个属性中。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class MetadataTypeBuddyAttribute : Attribute
{
    public MetadataTypeBuddyAttribute(Type modelType, Type buddyType)
    {
        TypeDescriptor.AddProviderTransparent(
           new AssociatedMetadataTypeTypeDescriptionProvider(
               modelType,
               buddyType
           ),
           modelType);
    }
}

在我的情况下(MVC4),我的界面上的数据注释属性已经起作用。这是因为我的模型直接实现了接口,而不是具有多级继承。但是,在接口级别实现的自定义验证属性不起作用。

仅在手动关联接口时,所有自定义验证才会相应地起作用。如果我理解你的情况,这也是你问题的解决方案。

[MetadataTypeBuddy(typeof(PersonViewModel), typeof(IPerson))] 
[MetadataTypeBuddy(typeof(PersonViewModel), typeof(IAddress))]
public class PersonViewModel : IPersonViewModel

答案 1 :(得分:1)

MetadataTypeBuddy属性对我不起作用。
但是在“启动”中添加“新” MetadataTypeBuddyAttribute确实可行,但会导致复杂的代码,使开发人员不知道将任何新类添加到“启动”中。

注意:每个类的应用程序启动时只需调用一次AddProviderTransparent。

这是为一个类添加多种元数据类型的线程安全方法。

library(data.table)
listings <- fread("listings.csv", drop=2)

答案 2 :(得分:0)

基于这里的答案,我无法以某种方式使MetadataTypeBuddy属性起作用。我确定我们必须设置一个MVC应该调用该属性的地方。当我在Application_Start()中手动运行该属性时,我设法让它工作了

new MetadataTypeBuddyAttribute(typeof(PersonViewModel), typeof(IPerson));
new MetadataTypeBuddyAttribute(typeof(PersonViewModel), typeof(IAddress));