Использование отражения для двунаправленного автоматического сопоставления всех доменных объектов для просмотра моделей в MVC3 - PullRequest
3 голосов
/ 04 ноября 2011

Играя с ASP.Net MVC 3 и изучая его, я работал с AutoMapper для сопоставления сущностей моего домена с моими моделями представления.

Я устал от индивидуального создания картыдля каждой ViewModel я реализовал.В результате я написал некоторый код для сканирования моей сборки и использовал некоторое отражение для создания каждого из необходимых отображений.Однако, поскольку я не очень хорошо знаком с лучшими практиками использования AutoMappers, я подумал, что смогу показать всем, что я сделал, и спросить, может ли мой подход вернуться назад, чтобы укусить меня.

По сути, яиметь класс с именем AutoMappingConfigurator (используется в Global.asax.cs) следующим образом:

public static class AutoMappingConfigurator
    {
        public static void Configure(Assembly assembly)
        {
            var autoMappingTypePairingList = new List<AutoMappingTypePairing>();

            foreach (Type t in assembly.GetTypes())
            {
                var autoMapAttribute = t
                    .GetCustomAttributes(typeof(AutoMapAttribute), true)
                    .OfType<AutoMapAttribute>()
                    .FirstOrDefault();

                if (autoMapAttribute != null)
                {
                    autoMappingTypePairingList
                .Add(new AutoMappingTypePairing(autoMapAttribute.SourceType, t));
                }
            }

            autoMappingTypePairingList
               .ForEach(mappingPair => mappingPair.CreateBidirectionalMap());
        }
    }

По сути, он выполняет сканирование сборки для всех типов, помеченных с помощью AutoMapAttribute, и для каждого из них.обнаруживается, что он создает двунаправленное отображение.

AutoMapAttribute - это простой атрибут, который я создал (основываясь на примерах, которые я нашел в Интернете), который я присоединяю к своей модели представления, чтобы указать, к какому объекту домена он привязан..

Например.

[AutoMap(typeof(Project))]
public class ProjectDetailsViewModel
{
    public int ProjectId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

Что касается двунаправленного отображения, до сих пор в моей работе с MVC3 я обнаружил, что мне часто кажется, что мне нужно сопоставить Entity с ViewModel дляHttpGet и из ViewModel в Entity для HttpPost.

Двунаправленное отображение реализуется какметод расширения следующим образом:

public static void CreateBidirectionalMap(this AutoMappingTypePairing mappingPair)
{
    Mapper.CreateMap(mappingPair.SourceType, mappingPair.DestinationType)
          .IgnoreProperties(mappingPair.DestinationType);

    Mapper.CreateMap(mappingPair.DestinationType, mappingPair.SourceType)
          .IgnoreProperties(mappingPair.SourceType);
}

Что касается метода расширения IgnoreProperties, я обнаружил, что всякий раз, когда у меня была модель представления, у которой было свойство, которое я хотел игнорировать (например, когда моя модель представления имеет раскрывающийся список, которыйне является частью базового доменного объекта) Мне, кажется, пришлось создать игнорирование вручную с помощью метода ForMember AutoMapper.Поэтому я создал еще один атрибут, чтобы указать, какие свойства нужно было игнорировать, чтобы мой код отражения в AutoMappingConfigurator мог сделать это для меня автоматически.

Метод расширения IgnoreProperties реализован как метод расширения следующим образом:

public static IMappingExpression IgnoreProperties(this IMappingExpression expression
                                                  , Type mappingType)
{
    var propertiesWithAutoMapIgnoreAttribute =
        mappingType.GetProperties()
            .Where(p => p.GetCustomAttributes(typeof(AutoMapIgnoreAttribute), true)
                         .OfType<AutoMapIgnoreAttribute>()
                         .Count() > 0);
    foreach (var property in propertiesWithAutoMapIgnoreAttribute)
    {
        expression.ForMember(property.Name, opt => opt.Ignore());
    }
    return expression;
}

Все это позволяет мне написать мою ViewModel следующим образом и иметь ее AutoMapped:

[AutoMap(typeof(EntityClass))]
private class ViewModelClass
{
    public int EntityClassId { get; set; }

    [AutoMapIgnore]
    public IEnumerable<SelectListItem> DropDownItems { get; set; }
}

private class EntityClass
{
    public int EntityClassId { get; set; }
}

Пока это работает для меня до сих пор, я боюсь, что он может вернуться, чтобы укусить меня из-замой низкий уровень опыта работы с AutoMapper.

Итак, мои вопросы:

  • Это хороший способ настроить AutoMapper для настройки моих сопоставлений между моими сущностями домена и моделями представления?
  • Есть ли что-то в AutoMapper, которое я могу пропустить, что сделает это плохим подходом?
  • Является ли хорошей идеей подключение свойства Игнорировать по отражению и атрибутам?
  • Является ли создание двунаправленной карты между моей сущностью и ViewModel хорошей идеей?

Ответы [ 3 ]

2 голосов
/ 22 ноября 2011

Я предпочитаю модели с однонаправленным обзором. Другими словами, я использую одну модель представления, когда я представляю данные пользователю, и другую, когда я обрабатываю, создает (и другую для обновлений и т. Д.).

Правда, таким образом вы получите больше объектов. Преимущество состоит в том, что вы избегаете ненужных свойств модели представления (и, следовательно, необходимости их игнорировать). Я думаю, что модель представления всегда должна быть максимально простой (обычные свойства get / set и, возможно, конструктор, если у вас есть список, который нужно инициализировать). Если вас беспокоят имена и типы данных «общих свойств» объектов, вы можете (хотя я не думаю, что вы должны) определять их в интерфейсе или базовом классе.

Атрибуты AutoMapIgnore в модели домена проблематичны, если вы хотите использовать определенное свойство модели домена в двух моделях представления. Я думаю, что это относится и к вашему решению.

Наконец, я не вижу большой пользы от использования атрибутов вместо строк кода, таких как

Mapper.CreateMap<SourceType, DestinationType>();

Это может быть проще? Возможно, было бы неплохо иметь метод расширения, который игнорирует «несопоставленные свойства» при отображении из модели представления в модель, позволяя записать

Mapper.CreateMap<SourceType, DestinationType>().IgnoreUnmappedProperties();

IMO, это проще, чем использование атрибутов AutoMapIgnore.

1 голос
/ 10 ноября 2011

Я не вижу ничего плохого в вашем подходе, и чтобы ответить на ваши вопросы:

  • ИМО, я считаю, что способ, которым вы настраиваете это, является хорошим способом создания в других отношениях утомительных отображений междуваши модели / DTO и ваши сущности.
  • Я ничего не знаю об AutoMapper, который сделал бы это плохим подходом.
  • Использование атрибутов для подключения свойства ignore - хорошая идея, атрибуты используются только для этого в MVC.
  • Двунаправленное отображение звучит как хорошая идея в большинстве ситуаций, но мне любопытно, как это будет работать с пользовательскими сопоставлениями.

Некоторые моменты, которые следует учитывать:

  1. Как он обрабатывает вложенные сопоставления?
  2. Как он обрабатывает пользовательские сопоставления?
    • Возможные атрибуты?
  3. Двунаправленное сопоставление и сопоставление каждой стороны с атрибутами
    • Какая из них обеспечивает большую ясность?
    • Какая из них обрабатываетпользовательские / вложенные отображения лучше?
  4. Влияет ли производительность?
    • Скорее всего, нет, но это может быть проблемой при использовании отражения.
0 голосов
/ 15 июля 2012

Если обе ваши модели и сущности имеют базовые классы, вы можете сделать что-то вроде следующего:

        var entityAssembly = typeof(BaseEntity).Assembly;
        var modelAssembly = typeof(BaseModel).Assembly;
        var modelNamespace = modelAssembly.GetTypes().Where(a => a.BaseType == typeof(BaseModel)).FirstOrDefault().Namespace;

        foreach (var entity in entityAssembly.GetTypes().Where(a=> a.BaseType == typeof(BaseEntity)))
        {
            var model = modelAssembly.GetType(String.Format("{0}.{1}{2}", modelNamespace, entity.Name, "Model"));
            if (model != null)
            {
                Mapper.CreateMap(entity, model);
                Mapper.CreateMap(model, entity);
            }
        }

Это двунаправленное соглашение о реализации конфигурации, и если соответствующая модель не существует, она переходит к следующему объекту. Это довольно просто, но является альтернативой ручной карте.

NB. Это делает предположение, что имена моделей и сущностей соответствуют какому-либо обычному наименованию. Например.

{EntityName} Модель экв. Ветвь к ВетвьМодель

Надеюсь, это поможет. Обратите внимание, что это реальная базовая реализация. Если моделей не существует, код выдает ошибку в строке № 3.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...