Используйте Automapper для сопоставления моделей больших доменов для объектов базы данных

StackOverflow https://stackoverflow.com/questions/3710402

Вопрос

Я хотел бы использовать Automapper, чтобы отобразить мои объекты модели с объектами базы данных. Скажем, объект базы данных составляет более 30 полей, и я хочу, чтобы карта 10 из 20 свойств из моей модели. Чтобы сделать его более сложным, мне нужно отображать разные свойства, когда я обновляю запись, чем когда я вставляю новую запись в базу данных.

Решение, которое я использую, заключается в создании 2 общих классов вставки и обновления и отображения профиля, который определяет оба отображения.

Пример ниже:

public abstract class SyncMappingWrapper<TFrom> where TFrom : class
{
    protected SyncMappingWrapper(TFrom model)
    {
        Model = model;
    }

    public TFrom Model { get; private set; }
}

public class Update<TFrom> : SyncMappingWrapper<TFrom> where TFrom : class
{
    public Update(TFrom model)
        : base(model)
    {
    }
}

public class Insert<TFrom> : SyncMappingWrapper<TFrom> where TFrom : class
{
    public Insert(TFrom model)
        : base(model)
    {
    }
}

Картирование, однако, становится неприятным, поскольку цикломатическая сложность идет на небо (более 50), поскольку мне нужно определить игнорирование () для всех свойств, которые я не сопоставляю:

CreateMap<Update<OracleModel>, LiveModel>()
            .ForMember(des => des.ApprovedBy, opt => opt.Ignore())
            .ForMember(des => des.ApprovedDate, opt => opt.Ignore())
            ...
            .ForMember(des => des.UNSPSC, opt => opt.Ignore())
            .ForMember(des => des.BaseUnit, opt => opt.MapFrom(src => src.Model.UOM.BaseUOM.PerSalesUnit))
            .ForMember(des => des.BaseUOM, opt => opt.MapFrom(src => src.Model.UOM.BaseUOM.UnitOfMeasure.Code))
            .ForMember(des => des.SalesUnit, opt => opt.MapFrom(src => src.Model.UOM.SalesUOM.PerSalesUnit))
            .ForMember(des => des.SalesUOM, opt => opt.MapFrom(src => src.Model.UOM.SalesUOM.UnitOfMeasure.Code))
            .ForMember(des => des.OrderUnit, opt => opt.MapFrom(src => src.Model.UOM.OrderUOM.PerSalesUnit))
            .ForMember(des => des.OrderUOM, opt => opt.MapFrom(src => src.Model.UOM.OrderUOM.UnitOfMeasure.Code))
            .ForMember(des => des.SalesPrice, opt => opt.MapFrom(src => src.Model.Price.Value))
            .ForMember(des => des.Alternate, opt => opt.Ignore())
            .ForMember(des => des.ManufacturerID, opt => opt.Ignore())
            .ForMember(des => des.ProductCode, opt => opt.MapFrom(src => src.Model.ProductCode))
            .ForMember(des => des.ProductName, opt => opt.MapFrom(src => src.Model.ProductName))
            .ForMember(des => des.ProductHTML, opt => opt.Ignore())
            .ForMember(des => des.Version, opt => opt.Ignore())
            ...
            .ForMember(des => des.UnitsOfMeasure2, opt => opt.Ignore())
            .ForMember(des => des.Manufacturer, opt => opt.Ignore());

Я решил проблему для вставки новой записи, создав новый объект:

CreateMap<Insert<OracleModel>, LiveModel>()
            .ConstructUsing(x => new LiveModel
                {
                    BaseUnit = x.Model.UOM.BaseUOM.PerSalesUnit,
                    BaseUOM = x.Model.UOM.BaseUOM.UnitOfMeasure.Code,
                    SalesUnit = x.Model.UOM.SalesUOM.PerSalesUnit,
                    SalesUOM = x.Model.UOM.SalesUOM.UnitOfMeasure.Code,
                    OrderUnit = x.Model.UOM.OrderUOM.PerSalesUnit,
                    OrderUOM = x.Model.UOM.OrderUOM.UnitOfMeasure.Code,
                    SalesPrice = x.Model.Price.Value,
                    LeadTime = x.Model.LeadTime,
                    ProductCode = x.Model.ProductCode,
                    ProductName = x.Model.ProductName,
                    SupplierCode = x.Model.SupplierCode,
                    Weight = x.Model.Weight
                })
            .ForAllMembers(xc => xc.Ignore());

Но это не работает для обновлений, где я хочу сопоставить свойства с существующим объектом, а не новым экземпляром:

        Mapper.Map(update, existingRecord);

Я бы предпочел избегать DynamicMap (), чтобы сохранить полный контроль над отображением (поэтому я не буду набирать случайное свойство по ошибке). Моя цель - разобраться с проблемой сложности цикломата. Пожалуйста, не предлагайте ValueInjecter или любые другие методы. Я ищу решение в Automapper.

Это было полезно?

Решение

Спустя пять лет, но вот предложение уменьшить эту проблему сложности картирования. Вы можете создать расширение, которое сначала поможет вам игнорировать всех участников:

public static class AutoMapperExtension {
    public static IMappingExpression<TSource, TDest> IgnoreAllMembers<TSource, TDest>(this IMappingExpression<TSource, TDest> expression) {
        expression.ForAllMembers(opt => opt.Ignore());
        return expression;
    }
}

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

Mapper.CreateMap<Insert<OracleModel>, LiveModel>()
      .IgnoreAllMembers()
      .ForMember(d => d.BaseUnit, o => o.MapFrom(s => s.Model.UOM.BaseUOM.PerSalesUnit))
      /* Mapping for other members here. */;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top