如何让映射的源和目标成员在AutoMapper中创建自定义映射?

时间:2016-02-11 09:13:25

标签: c# .net automapper dto

例如,让我们说我需要映射这些类:

public class Dto 
{
     public List<string> Items { get; set; } = new List<string> { "orange", "apple" }
}

public class DomainObject
{
     public List<string> Items { get; set; }
}

...我希望使用AutoMapper配置自定义映射,在执行必须存在DomainObjectDto的某些操作后返回DomainObject,或者至少我应该能够同时获得来源Items和目标Items

为什么我需要这个?

因为我将DTO映射到更改跟踪的对象,我需要提供一种自定义方法来映射集合,以便在映射操作之前不会丢失跟踪信息。

目前,我已经使用扩展方法在代码中实现了整个集合映射,但我想将此扩展方法称为AutoMapper配置的一部分,以保持代码尽可能简单并保持干燥

可能的部分解决方案

我发现AutoMapper有以下IMappingExpression<TSource, TDestination>方法:

IMappingExpression<TSource, TDestination> AfterMap(Action<TSource, TDestination> afterFunction); 

虽然这解决了我90%的问题,但仍然存在问题。 AfterMap未提供当前IMapper 的实例。我不再使用静态Mapper,但我在Castle Windsor上为IMapper配置了一个自定义工厂,因此我可以将它注入任何地方。

我还需要一个当前IMapper的实例。我可以解决IMapper a la Service Locator反模式,但我想知道AutoMapper是否还有另一种方法可以执行已经执行的操作AfterMap它可以以某种方式提供当前IMapper ......

1 个答案:

答案 0 :(得分:0)

在映射配置期间使用AfterMap并非100%满足解决方案

这是我的实际情况:

mapperConfig.CreateMap<CustomerUpdateDto, Customer>()
                .ForMember(c => c.Locations, m => m.Ignore())
                .ForMember(c => c.Aliases, opts => opts.Ignore())
                .ForMember(c => c.Contacts, opts => opts.Ignore())
                .ForMember(c => c.Activities, opts => opts.Ignore())
                .AfterMap
                (
                    (dto, customer) =>
                    {
                        // AHHH! Dependency injection, please ;)
                        IMapper mapper = Container.Current.Resolve<IMapper>();

                        dto.Aliases.MapTo<ISet<CustomerAlias>, ISet<CustomerAlias>, CustomerAlias>(customer.Aliases, mapper);
                        dto.Activities.MapTo<ISet<Activity>, ISet<Activity>, Activity>(customer.Activities, mapper);
                        dto.Locations.MapTo<ISet<Location>, ISet<Location>, Location>(customer.Locations, mapper);
                    }
                )
                .ForAllMembers(options => options.Condition(src => !src.IsSourceValueNull));

MapTo是一个自定义扩展方法,它概括了一个解决方案,以避免创建整个集合的新实例的默认AutoMapper行为,而不是能够将每个集合项从源映射到目标对象:

public static class CollectionMappingExtensions
{
    public static void MapTo<TSource, TTarget, TItem>(this TSource source, TTarget target, IMapper mapper)
        where TSource : ICollection<TItem>
        where TTarget : ICollection<TItem>
    {
        foreach (TItem item in source)
        {
            TItem targetItem = target.SingleOrDefault(someItem => someItem.Equals(someItem));

            if (targetItem == null)
                target.Add(item);
            else
                mapper.Map(item, targetItem);
        }
    }
}

BTW,如果我可以避免使用静态服务定位器而不是依赖注入来获取IMapper,那就太棒了。