将基数 class 映射到集合的具体实现

Map base class to specific implementation for collections

是否可以使用自动映射器从源对象映射到目标对象的特定实现?

在下面的代码示例中,我想从 SourceItem 映射到 TargetSampleItem 而不是 TargetBaseItem。重要提示:TargetBaseItem 不能是抽象的。但是,如果我使用以下映射器配置,则始终使用 TargetBaseItem

总而言之,以下集合应包含 TargetSampleItem 类型的项目,该类型源自 TargetBaseItem

public IList<TargetItemBase> Items { get; set; } = new List<TargetItemBase>();

完整代码

using AutoMapper;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MapperTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var configuration = new MapperConfiguration(cfg =>
            {
                cfg.CreateMap<SourceRoot, TargetRoot>();

                cfg.CreateMap<SourceItem, TargetItemBase>()
                    .IncludeAllDerived();
            });

            configuration.AssertConfigurationIsValid();
            var mapper = configuration.CreateMapper();

            var source = new SourceRoot
            {
                Id = 1,
                Items = new List<SourceItem>
                {
                    new SourceItem { A = "a1", B = "b1" },
                    new SourceItem { A = "a2", B = "b2" }
                }
            };

            var result = mapper.Map<TargetRoot>(source);

            // Should retur true
            Console.WriteLine(result.Items.First() is TargetSampleItem);
            Console.ReadLine();
        }

        /// <summary>
        /// Source model
        /// </summary>
        public class SourceRoot
        {
            public int Id { get; set; }
            public IList<SourceItem> Items { get; set; } = new List<SourceItem>();
        }

        public class SourceItem
        {
            public string A { get; set; }
            public string B { get; set; }
        }

        /// <summary>
        /// Target model
        /// </summary>
        public class TargetRoot
        {
            public int Id { get; set; }

            public IList<TargetItemBase> Items { get; set; } = new List<TargetItemBase>();
        }

        public class TargetItemBase
        {
            public string A { get; set; }
        }

        public class TargetSampleItem : TargetItemBase
        {
            public string B { get; set; }
        }
    }
}

编辑

使用 As<> 无效,因为 AutoMapper 没有映射到类型,而不仅仅是强制转换它:

cfg.CreateMap<SourceItem, TargetItemBase>()
   .As<TargetSampleItem>();

编辑 2/解决方案

使用 As<> 有效,如果 SourceItemTargetSampleItem 之间的映射也被添加:

cfg.CreateMap<SourceItem, TargetItemBase>().As<TargetSampleItem>();
cfg.CreateMap<SourceItem, TargetSampleItem>();

如果 As<> 对您不起作用,那么可能的解决方案可能是使用 AfterMap,例如 -

CreateMap<SourceRoot, TargetRoot>()
    .AfterMap((s, d) =>
    {
        s.Items.ToList().ForEach(p => d.TargetItems.Add(new TargetSampleItem { A = p.A, B = p.B }));
    });

(这不是一个优雅的解决方案,但由于 TargetSampleItem 不是任何地图的目标,我看不出 AutoMapper 会创建它的实例的任何原因)。

您必须重命名 Items 属性中的任何一个,以便 AutoMapper 不会尝试自动映射它们(我将 TargetRoot class 中的属性重命名为 TargetItems).当然,您不需要 SourceItemTargetItemBase 之间的映射。

As 对我有用:

cfg.CreateMap<SourceItem, TargetItemBase>().As<TargetSampleItem>();
cfg.CreateMap<SourceItem, TargetSampleItem>();