Model view controller – uses multiple interfaces with MVC dataannotations and metadatatype
I am using data annotations to apply validation to MVC ViewModel, which is a combination of several Entity Framework objects and some custom logic Validation has been defined for entity objects in the interface, but how can this validation be applied to the ViewModel?
My original idea was to combine the interfaces into one and apply the combined interface to the ViewModel, but it didn't work Here are some sample codes that show what I mean:
// 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; } }
My real-world problem involves about 150 attributes on the ViewModel, so it is not as trivial as the sample. Re entering all attributes seems to be a terrible violation of dry
Any ideas on how to achieve this goal?
Solution
To make it work, you need to manually associate the interface as metadata of a concrete class
I want to be able to add multiple metadatatype attributes, but this is not allowed
[AttributeUsage(AttributeTargets.Class,AllowMultiple = false)] // Notice AllowMultiple public sealed class MetadataTypeAttribute : Attribute
Therefore, this gives a compilation error:
[MetadataType(typeof(IPerson))] [MetadataType(typeof(IAddress))] // <--- Duplicate 'MetadataType' attribute public class Personviewmodel : IPersonviewmodel
However, only one interface works So my solution is to simply use the associatedmetadatatypedescriptionprovider to associate the interface and wrap it in another property
[AttributeUsage(AttributeTargets.Class,AllowMultiple = true)] public class MetadataTypeBuddyAttribute : Attribute { public MetadataTypeBuddyAttribute(Type modelType,Type buddyType) { TypeDescriptor.AddProviderTransparent( new AssociatedMetadataTypeTypeDescriptionProvider( modelType,buddyType ),modelType); } }
In my case (mvc4), the data annotation attribute on my interface has worked This is because my model implements the interface directly, not with multi-level inheritance However, custom validation properties implemented at the interface level do not work
Only when you manually associate interfaces will all custom validation work accordingly If I understand your situation, this is also the solution to your problem
[MetadataTypeBuddy(typeof(Personviewmodel),typeof(IPerson))] [MetadataTypeBuddy(typeof(Personviewmodel),typeof(IAddress))] public class Personviewmodel : IPersonviewmodel