使用开放式泛型自动化并在ForMember语句中包含源代码

时间:2016-08-16 19:49:58

标签: generics automapper open-generics automapper-5

我最近从Automapper 4.2.1升级到5.1.1,并且遇到了以前有效的涉及开放式泛型的映射问题。

以前,在automapper配置中,我有以下开放的通用映射配置

CreateMap(typeof(IPager<>), typeof(ModelPager<>))
    .ForMember("Items", e => e.MapFrom(o => (IEnumerable) o));

这适用于Automapper 4,但在尝试通过InvalidOperaionException进行映射时,在IMapper.Map<TDestination>(source)中失败了5。执行 Items ForMember操作的映射时出现失败,并显示“ Sequence包含无匹配元素”的异常消息

如下面的示例实现代码所示 IPager<TSource>实现IEnumerable<TSource>Items的{​​{1}}属性为ModelPager<TDestination>,因此广告素材应该有效。并且每个IEnumerable<TDestination>TSource

都存在有效的映射
TDestination

IPager 界面

CreateMap<TSource, TDestination>();

IPager 实施

public interface IPager<out TItem> : IEnumerable<TItem>
{
    int CurrentPage { get; }

    int PageCount { get; }

    int PageSize { get; }

    int TotalItems { get; }
}

ModelPager

public class Pager<TItem> : IPager<TItem>
{
    private readonly IEnumerable<TItem> _items;

    public Pager(IEnumerable<TItem> items,
                 int currentPage,
                 int pageSize,
                 int totalItems)
    {
        /// ... logic ... 
        this._items = items ?? Enumerable.Empty<TItem>();
        this.CurrentPage = currentPage;
        this.PageSize = pageSize;
        this.TotalItems = totalItems;
    }

    public int CurrentPage { get; }

    public int PageCount => (this.TotalItems + this.PageSize - 1) / this.PageSize;

    public int PageSize { get; }

    public int TotalItems { get; }

    IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();

    public IEnumerator<TItem> GetEnumerator() => this._items.GetEnumerator();
}

在Automapper 5中通过显式映射每个可能的映射放弃open generics,或者使用需要我手动映射所有属性的自定义开放式通用type converter,在Automapper 5中映射此内容的正确方法是什么?并使用反射来解析分配的开放类型?

1 个答案:

答案 0 :(得分:4)

鉴于这看起来像是一个错误(AutoMapper #1624),可以使用需要反射的自定义开放式通用TypeConverter来解决此问题。

映射应该改为

CreateMap(typeof(IPager<>), typeof(ModelPager<>))
    .ConvertUsing(typeof(PagerToModelPagerConverter<,>));

使用自定义ITypeConverter

public class PagerToModelPagerConverter<TSource, TDestination> : ITypeConverter<IPager<TSource>, ModelPager<TDestination>>
{
    public ModelPager<TDestination> Convert(IPager<TSource> source,
                                            ModelPager<TDestination> destination,
                                            ResolutionContext context)
    {
        var list = source.ToList(); // avoid redundant iterations
        var itemMapping = context.Mapper.Map<IEnumerable<TSource>, IEnumerable<TDestination>>(list);

        var modelPager = new ModelPager<TDestination>
                         {
                             CurrentPage = source.CurrentPage,
                             Items = itemMapping,
                             PageCount = source.PageCount,
                             PageSize = source.PageSize,
                             TotalItems = source.TotalItems
                         };

        return modelPager;
    }