Использование нескольких интерфейсов с MVC DataAnnotations и MetaDataType - PullRequest
3 голосов
/ 19 октября 2011

Я применяю проверку с использованием 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; }
}

Моя реальная проблема включает в себя около 150 свойств в ViewModel, так что это не так тривиально, как пример, и перепечатывание всех свойств кажется ужасным нарушениемСУХОЙ.

Есть идеи, как этого добиться?

Ответы [ 2 ]

7 голосов
/ 13 июня 2014

Чтобы это работало, вам нужно вручную связать интерфейсы как метаданные для ваших конкретных классов.

Я ожидал, что смогу добавить несколько атрибутов 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
0 голосов
/ 12 января 2017

исходя из ответа здесь, я не мог как-то заставить этот атрибут MetadataTypeBuddy работать.Я уверен, что мы должны где-то установить, что MVC должен вызывать этот атрибут.Мне удалось заставить его работать, когда я запускаю этот атрибут вручную в Application_Start (), как это

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