Automapper 通过继承映射基类

2024-01-27

我在使用自动映射器映射我的父类时遇到一些问题。 给定以下类,我创建了一个映射配置文件。

映射类:

public class SourceClass
{
    public int SourceProperty1 { get; set; }
    public int SourceProperty2 { get; set; }
    public string SourceProperty3 { get; set; }
    public string SourceProperty4 { get; set; }
}

public class TargetBaseClass
{
    public int TargetProperty1 { get; set; }
    public int TargetProperty2 { get; set; }
}

public class TargetClass1: TargetBaseClass
{
    public string TargetProperty3 { get; set; }
}

public class TargetClass2: TargetBaseClass
{
    public string TargetProperty4 { get; set; }
}

Map:

public class MappingProfile: Profile
{
    protected override void Configure()
    {
        CreateMap<SourceClass, TargetBaseClass>()
            .Include<SourceClass, TargetClass1>()
            .Include<SourceClass, TargetClass2>()
            .ForMember(dst => dst.TargetProperty1, opt => opt.MapFrom(src => src.SourceProperty1))
            .ForMember(dst => dst.TargetProperty2, opt => opt.MapFrom(src => src.SourceProperty2));
        CreateMap<SourceClass, TargetClass1>()
            .ForMember(dst => dst.TargetProperty3, opt => opt.MapFrom(src => src.SourceProperty3));
        CreateMap<SourceClass, TargetClass2>()
            .ForMember(dst => dst.TargetProperty4, opt => opt.MapFrom(src => src.SourceProperty4));
    }
}

最后我的程序:

static void Main(string[] args)
{
    Mapper.Initialize(x => x.AddProfile<MappingProfile>());
    var sourceClass = new SourceClass
        {
            SourceProperty1 = 1,
            SourceProperty2 = 2,
            SourceProperty3 = "3",
            SourceProperty4 = "4"
        };
    var targetBaseClass = Mapper.Map<TargetBaseClass>(sourceClass);
    var targetClass1 = Mapper.Map<TargetClass1>(sourceClass);
    var targetClass2 = Mapper.Map<TargetClass2>(sourceClass);

    Console.WriteLine("TargetBaseClass: {0} {1}", targetBaseClass.TargetProperty1,
                      targetBaseClass.TargetProperty2); //1 2
    Console.WriteLine("TargetClass1: {0} {1} {2}", targetClass1.TargetProperty1, targetClass1.TargetProperty2,
                      targetClass1.TargetProperty3);//0 0 3 ???
    Console.WriteLine("TargetClass2: {0} {1} {2}", targetClass2.TargetProperty1, targetClass2.TargetProperty2,
                      targetClass2.TargetProperty4);//1 2 4
}

问题是,当我尝试映射到派生类时,我的父类的属性将不会被映射TargetClass1 but it will for TargetClass2。谁能向我解释我做错了什么,以及为什么这两张地图的行为不同? (我的顺序是Include事情?)

Edit:经仔细检查,订单does确实很重要。不过我还是不明白为什么只有第二个Include会算的。

Edit2:根据@GruffBunny 的评论,我认为我可以通过使用扩展方法来“修复”这个问题。然而,我真的不明白他们为什么这样做。查看代码AutoMapper.TypeMap,我可以清楚地看到这一点:

public void IncludeDerivedTypes(Type derivedSourceType, Type derivedDestinationType)
{
    _includedDerivedTypes[derivedSourceType] = derivedDestinationType;
}

显然,这意味着您只能为每个包含的源类型指定一个目标。然而,我没有看到任何东西会阻止他们支持不止一种目的地类型。


你可以看看这个自定义扩展方法 http://matthewmanela.com/blog/update-on-inheriting-base-type-mappings-with-automapper/。源代码也可以在Github上找到here https://github.com/mmanela/InheritedAutoMapper/tree/master/MappingExtensions.

扩展方法有我现在能想到的树缺点。首先是Mapper.AssertConfigurationIsValid()将失败,因为它找不到基本映射中定义的属性映射。解决方案是忽略底映射中定义的任何提供的成员映射。

第二是扩展方法依赖于静态Mapper班级。如果这就是您使用 AutoMapper 的方式,那么就没有问题。如果您有多个映射引擎和/或针对 AutoMapper 接口编写代码,则无法使用此扩展方法。为了支持这两种情况,我们需要添加两个可选参数:IConfigurationProvider and IMappingEngine.

我尽量避免使用静态Mapper类并通过一个方法在我需要的地方注入接口IoC https://stackoverflow.com/questions/3058/what-is-inversion-of-control容器。

第三个是扩展方法不返回IMappingExpression<TSource, TDestination>并禁止覆盖基本成员映射。为了解决这个问题,我们返回IMappingExpression<TSource, TDestination>并取消所有成员的条件。

这会产生以下代码:

public enum WithBaseFor
{
    Source,
    Destination,
    Both
}

public static class AutoMapperExtensions
{
    public static IMappingExpression<TSource, TDestination> InheritMappingFromBaseType<TSource, TDestination>(
        this IMappingExpression<TSource, TDestination> mappingExpression,
        WithBaseFor baseFor = WithBaseFor.Both,
        IMappingEngine mappingEngine = null,
        IConfigurationProvider configurationProvider = null)
    {
        Type sourceType = typeof (TSource);
        Type destinationType = typeof (TDestination);

        Type sourceParentType = baseFor == WithBaseFor.Both || baseFor == WithBaseFor.Source
            ? sourceType.BaseType
            : sourceType;

        Type destinationParentType = baseFor == WithBaseFor.Both || baseFor == WithBaseFor.Destination
            ? destinationType.BaseType
            : destinationType;

        mappingExpression
            .BeforeMap((sourceObject, destObject) =>
            {
                if (mappingEngine != null)
                    mappingEngine.Map(sourceObject, destObject, sourceParentType, destinationParentType);
                else
                    Mapper.Map(sourceObject, destObject, sourceParentType, destinationParentType);
            });

        TypeMap baseTypeMap = configurationProvider != null
            ? configurationProvider.FindTypeMapFor(sourceParentType, destinationParentType)
            : Mapper.FindTypeMapFor(sourceParentType, destinationParentType);

        if (baseTypeMap == null)
        {
            throw new InvalidOperationException(
                string.Format("Missing map from {0} to {1}.", new object[]
                {
                    sourceParentType.Name,
                    destinationParentType.Name
                }));
        }

        foreach (PropertyMap propertyMap in baseTypeMap.GetPropertyMaps())
            mappingExpression.ForMember(propertyMap.DestinationProperty.Name, opt => opt.Ignore());

        return mappingExpression;
    }
}

Usage

CreateMap<SourceClass, TargetBaseClass>()
    .ForMember(dst => dst.TargetProperty1, opt => opt.MapFrom(src => src.SourceProperty1))
    .ForMember(dst => dst.TargetProperty2, opt => opt.MapFrom(src => src.SourceProperty2));

CreateMap<SourceClass, TargetClass1>()
    .ForMember(dst => dst.TargetProperty3, opt => opt.MapFrom(src => src.SourceProperty3))
    .InheritMappingFromBaseType(WithBaseFor.Destination)
    ;

CreateMap<SourceClass, TargetClass2>()
    .ForMember(dst => dst.TargetProperty4, opt => opt.MapFrom(src => src.SourceProperty4))
    .InheritMappingFromBaseType(WithBaseFor.Destination)
    ;

也许仍然有一些场景没有涵盖,但它肯定可以解决您的问题,这样您就不需要编写特定的扩展方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Automapper 通过继承映射基类 的相关文章

  • 我可以使用反射更改 C# 中的私有只读字段吗?

    我想知道 由于很多事情都可以使用反射完成 我可以在构造函数完成执行后更改私有只读字段吗 注 只是好奇 public class Foo private readonly int bar public Foo int num bar num
  • Web UI 中的 .Result 出现死锁

    我正在阅读以下主题http blog stephencleary com 2012 07 dont block on async code html http blog stephencleary com 2012 07 dont bloc
  • 如何知道并加载特定文件夹中的所有图像?

    我有一个应用程序 C Builder 6 0 需要知道特定文件夹中的图像总数 然后我必须加载它们 在 ImageList 或 ComboBoxEx 中 或任何其他控件中 我怎样才能做到这一点 我知道如何在控件中加载图像 或保存在 TList
  • LINQ to XML - 如何正确使用 XDocument

    现在我首先要说的是 这确实是一项任务 然而 在我遇到 Linq to XML 语法之前 我几乎已经完成了它 我有 2 个课程 曲目和 CD 现在作为作业的一部分 我创建了一张 CD 然后向其中添加了一些曲目 在搜索了大量完美解释了如何从 x
  • 有没有比这更快的方法来查找目录和所有子目录中的所有文件?

    我正在编写一个程序 需要在目录及其所有子目录中搜索具有特定扩展名的文件 这将在本地驱动器和网络驱动器上使用 因此性能是一个问题 这是我现在使用的递归方法 private void GetFileList string fileSearchP
  • 我应该在单元测试中使用 AutoMapper 吗?

    我正在为 ASP NET MVC 控制器方法编写单元测试 这些控制器依赖于IMapper 我创建的用于抽象 AutoMapper 的接口 使用 Castle Windsor 通过构造函数注入传入 动作方法使用IMapper从领域对象映射到
  • Linq Where 本地计数器关闭在 VS watch 中的结果不同

    我尝试删除前 3 个元素array与 LinQWhere扩展功能 这是一个例子 var array new 1 2 3 4 5 6 7 8 9 var count 3 var deletedTest1 0 var test1 array W
  • 字节到二进制字符串 C# - 显示所有 8 位数字

    我想在文本框中显示一个字节 现在我正在使用 Convert ToString MyVeryOwnByte 2 但是 当字节开头有 0 时 这些 0 就会被删除 例子 MyVeryOwnByte 00001110 Texbox shows g
  • 阅读 Stack Overflow RSS 源

    我正在尝试获取未回答问题的列表the feed https stackoverflow com feeds 但我在阅读时遇到困难 const string RECENT QUESTIONS https stackoverflow com f
  • 根据拦截和返回值自动重试客户端WCF调用

    是否可以拦截 WCF 调用的结果并重试该操作 例如 操作的返回值可能包含状态代码 指示我传递到原始调用的会话令牌已过期 在这种情况下 我可以检索新的会话令牌并使用新的会话令牌重试调用 是否可以通过使用 WCF 拦截返回值 检查它 然后以对操
  • for 循环 - 没有效果的语句

    由于某种原因 我收到错误 statement with no effect关于这个声明 for j idx j lt iter j increment printf from loop idx i int idx punc ctxt j 你
  • 标准 C 中的 sizeof 与 sizeof()? [复制]

    这个问题在这里已经有答案了 我看到一些直接使用 sizeof 的代码 想知道它是否是标准 C 令我惊讶的是 它运行得很好 这是一个例子 include
  • 使用 AutoMapper 进行 LINQ GroupBy 聚合

    试图让查询工作 但老实说不确定如何 或者是否可能 进行它 因为我尝试过的一切都不起作用 共查询6个表 Person PersonVote PersonCategory Category City FirstAdminDivision Per
  • 如何在 C++ 中使用 PI 常数

    我想在一些 C 程序中使用 PI 常数和三角函数 我得到三角函数include
  • 如何使用 Clang 查找内存泄漏

    我在我的机器 ubuntu 中安装了 Clang 以便发现我的 C 代码中的内存泄漏 我编写了一个示例代码来检查它的工作情况 如下所示 File hello c for leak detection include
  • 如何从枚举中选择随机值?

    给定 C 中的任意枚举 如何选择随机值 我没有找到这个非常基本的问题 我会在一分钟内发布我的答案作为任何人的参考 但请随意发布你自己的答案 Array values Enum GetValues typeof Bar Random rand
  • 通过 MSBuild 调用 cl.exe 时无限期挂起

    我正在尝试在我的 主要是 C 项目上运行 MSBuild 想象一下一个非常庞大的代码库 Visual Studio 2015 是有问题的工具集 Windows 7 SP1 和 VS 2015 更新 2 即使使用 m 1 从而迫使它仅使用一个
  • C# - 为什么我需要初始化 [Out] 参数

    我有几个从本机 dll 导入的方法 使用以下语法 internal static class DllClass DllImport Example dll EntryPoint ExampleFunction public static e
  • 在 unix 中编译 dhrystone 时出错

    我是使用基准测试和 makefile 的新手 我已经从下面的链接下载了 Dhrystone 基准测试 我正在尝试编译它 但我遇到了奇怪的错误 我尝试解决它 但没有成功 有人可以帮助我运行 dhrystone 基准测试吗 以下是我尝试编译的两
  • 如何仅更改 DateTime 的日期部分,同时保留时间部分?

    我在代码中使用了很多 DateTime 我想将这些日期时间更改为我的特定日期并保留 时间 1 2012 02 02 06 00 00 gt 2015 12 12 06 00 00 2 2013 02 02 12 00 00 gt 2015

随机推荐