Automapper 5.2(当前最新)如果在Base Data Transfer Object的映射中配置,则忽略ExplicitExpansion()配置.但是,如果直接在Derived DTO中配置了映射,它仍然可以正常工作.我有一对DTO类,它们在字段集和映射配置中包含很多重复,我试图将它隔离到公共基础DTO类,但是这个问题阻止了我这样做.

下面的代码说明了这种奇怪的行为.有四个测试,其中两个未能断言未扩展基础DTO的属性.如果我将1-1..1-4行移动到2.1,则所有测试都通过.

我是否错过了一些代码或者这是Automapper中的错误,我必须向Automapper的错误跟踪器报告此问题?或者它可能是“按设计”,但为什么呢? (Ivan Stoev提出了一个有效的解决方案,但请允许我推迟接受答案,因为我面临的问题不是那么简单,我在下面的更新中添加了更多细节).

的UnitTest1.cs:

using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace AutoMapperIssue 
{
    public class Source { public string Name; public string Desc; }
    public class DtoBase              { public string Name { get; set; } }
    public class DtoDerived : DtoBase { public string Desc { get; set; } }
    [TestClass] public class UnitTest1
    {
        [AssemblyInitialize] public static void AssemblyInit(TestContext context)
        {
            Mapper.Initialize(cfg =>
            {
                cfg.CreateMap<Source,DtoBase>()
                    .ForMember(dto => dto.Name,conf => { // line 1-1
                        conf.MapFrom(src => src.Name);    // line 1-2
                        conf.ExplicitExpansion();         // line 1-3
                    })                                    // line 1-4
                    .Include<Source,DtoDerived>();
                cfg.CreateMap<Source,DtoDerived>()
                    // place 2.1
                    .ForMember(dto => dto.Desc,conf => {
                        conf.MapFrom(src => src.Desc);
                        conf.ExplicitExpansion();
                    });
            });
            Mapper.Configuration.CompileMappings();
            Mapper.AssertConfigurationIsValid();
        }

        private readonly IQueryable<Source> _iq = new List<Source> {
            new Source() { Name = "Name1",Desc = "Descr",},} .AsQueryable();

        [TestMethod] public void ProjectAll_Success() 
        {
            var projectTo = _iq.ProjectTo<DtoDerived>(_ => _.Name,_ => _.Desc);
            Assert.AreEqual(1,projectTo.Count()); var first = projectTo.First();
            Assert.IsNotNull(first.Desc); Assert.AreEqual("Descr",first.Desc);
            Assert.IsNotNull(first.Name); Assert.AreEqual("Name1",first.Name);
        }
        [TestMethod] public void SkipDerived_Success() 
        {
            var projectTo = _iq.ProjectTo<DtoDerived>(_ => _.Name);
            Assert.AreEqual(1,projectTo.Count()); var first = projectTo.First();
            Assert.IsNotNull(first.Name); Assert.AreEqual("Name1",first.Name);
            Assert.IsNull(first.Desc,"Should not be expanded.");
        }
        [TestMethod] public void SkipBase_Fail() 
        {
            var projectTo = _iq.ProjectTo<DtoDerived>(_ => _.Desc);
            Assert.AreEqual(1,first.Desc);
            Assert.IsNull(first.Name,"Should not be expanded. Fails here. Why?");
        }
        [TestMethod] public void SkipAll_Fail() 
        {
            var projectTo = _iq.ProjectTo<DtoDerived>();
            Assert.AreEqual(1,projectTo.Count()); var first = projectTo.First();
            Assert.IsNull(first.Desc,"Should not be expanded.");
            Assert.IsNull(first.Name,"Should not be expanded. Fails here. Why?");
        }
    }
}

packages.config:

<package id="AutoMapper" version="5.2.0" targetFramework="net452" />

UPD. Ivan Stoev全面回答了如何解决上面编码的问题.除非我被迫使用字段名称的字符串数组而不是MemberExpressions,否则它的效果非常好.这与这种方法与Value类型的成员(例如int,int?)崩溃有关.在下面的第一个单元测试中演示了崩溃堆栈跟踪.我会在另一个问题中询问它,或者更确切地说是在bug跟踪器中创建一个问题,因为崩溃肯定是一个bug.

UnitTest2.cs – 修复了Ivan Stoev的回答

using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace AutoMapperIssue.StringPropertyNames 
{   /* int? (or any ValueType) instead of string - .ProjectTo<> crashes on using MemberExpressions in projction */
    using NameSourceType = Nullable<int> /* String */; using NameDtoType = Nullable<int> /* String */;
    using DescSourceType = Nullable<int> /* String */; using DescDtoType = Nullable<int> /* String*/;

    public class Source
    {   
        public NameSourceType Name { get; set; }
        public DescSourceType Desc { get; set; }
    }

    public class DtoBase              { public NameDtoType Name { get; set; } }
    public class DtoDerived : DtoBase { public DescDtoType Desc { get; set; } }

    static class MyMappers
    {
        public static IMappingExpression<TSource,TDestination> Configure<TSource,TDestination>(this IMappingExpression<TSource,TDestination> target)
            where TSource : Source
            where TDestination : DtoBase
        {
            return target.ForMember(dto => dto.Name,conf =>
                {
                    conf.MapFrom(src => src.Name);
                    conf.ExplicitExpansion();
                });
        }
    }

    [TestClass] public class UnitTest2
    {
        [ClassInitialize] public static void ClassInit(TestContext context)
        {
            Mapper.Initialize(cfg =>
            {
                cfg.CreateMap<Source,DtoBase>()
                    .Configure()
                    .Include<Source,DtoDerived>()
                    .Configure()
                    .ForMember(dto => dto.Desc,conf => {
                        conf.MapFrom(src => src.Desc);
                        conf.ExplicitExpansion();
                    })
                ;
            });
            Mapper.Configuration.CompileMappings();
            Mapper.AssertConfigurationIsValid();
        }

        private static readonly IQueryable<Source> _iq = new List<Source> {
            new Source() { Name = -25 /* "Name1" */,Desc = -12 /* "Descr" */,} .AsQueryable();

        private static readonly Source _iqf = _iq.First();

        [TestMethod] public void ProjectAllWithMemberExpression_Exception() 
        {
            _iq.ProjectTo<DtoDerived>(_ => _.Name,_ => _.Desc); // Exception here,no way to use Expressions with current release
//Test method AutoMapperIssue.StringPropertyNames.UnitTest2.ProjectAllWithMemberExpression_Exception threw exception: 
//System.NullReferenceException: Object reference not set to an instance of an object.
//
//    at System.Linq.Enumerable.<SelectManyIterator>d__16`2.MoveNext()
//   at System.Linq.Enumerable.<distinctIterator>d__63`1.MoveNext()
//   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
//   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
//   at AutoMapper.QueryableExtensions.ProjectionExpression.To[TResult](IDictionary`2 parameters,IEnumerable`1 memberPathsToExpand)
//   at AutoMapper.QueryableExtensions.ProjectionExpression.To[TResult](Object parameters,Expression`1[] membersToExpand)
//   at AutoMapper.QueryableExtensions.Extensions.ProjectTo[TDestination](IQueryable source,IConfigurationProvider configuration,Object parameters,Expression`1[] membersToExpand)
//   at AutoMapperIssue.StringPropertyNames.UnitTest2.ProjectAllWithMemberExpression_Exception() in D:\01\AutoMapperIssue\UnitTest2.cs:line 84
        }
#pragma warning disable 649
        private DtoDerived d;
#pragma warning restore 649
        [TestMethod] public void ProjectAll_Fail() 
        {
            var projectTo = _iq.ProjectTo<DtoDerived>(null,new string[] { nameof(d.Name),nameof(d.Desc) } /* _ => _.Name,_ => _.Desc */);
            Assert.AreEqual(1,projectTo.Count()); var first = projectTo.First();
            Assert.IsNotNull(first.Desc,"Should be expanded.");                  Assert.AreEqual(_iqf.Desc,first.Desc);
            Assert.IsNotNull(first.Name,"Should be expanded. Fails here,why?"); Assert.AreEqual(_iqf.Name,first.Name);
        }
        [TestMethod] public void BaSEOnly_Fail() 
        {
            var projectTo = _iq.ProjectTo<DtoDerived>(null,new string[] { nameof(d.Name) } /* _ => _.Name */);
            Assert.AreEqual(1,"Should NOT be expanded.");
            Assert.IsNotNull(first.Name,first.Name);

        }
        [TestMethod] public void DerivedOnly_Success() 
        {
            var projectTo = _iq.ProjectTo<DtoDerived>(null,new string[] { nameof(d.Desc) } /* _ => _.Desc */);
            Assert.AreEqual(1,"Should be expanded."); Assert.AreEqual(_iqf.Desc,"Should NOT be expanded.");
        }
        [TestMethod] public void SkipAll_Success() 
        {
            var projectTo = _iq.ProjectTo<DtoDerived>(null,new string[] { });
            Assert.AreEqual(1,"Should NOT be expanded.");
            Assert.IsNull(first.Name,"Should NOT be expanded.");
        }
    }
}

UPD2.上面更新的问题肯定无法在外面修复,请参阅接受的答案下的评论.这是AutoMapper本身的问题.如果您迫不及待地想要修复更新的问题,可以使用以下简单(但不是次要)差异来制作AutoMapper补丁:https://github.com/moudrick/AutoMapper/commit/65005429609bb568a9373d7f3ae0a535833a1729

解决方法

Have I missed some piece of code

你没有错过任何东西.

or is this a bug in Automapper and I have to report this issue to Automapper’s bug tracker? Or is it probably “by design”,but why?

我怀疑这是“按设计”,很可能是错误或不完整的快速和脏实施.可以在PropertyMap类的ApplyInheritedPropertyMap方法的source code内部看到,该方法负责组合基本属性和派生属性配置. “继承的”映射属性目前是:

> CustomExpression
> Customresolver
>条件
>前提条件
> NullSubstitute
> MappingOrder
> ValueResolverConfig

而以下(基本上所有bool类型)属性(包括有问题的属性)不是:

> AllowNull
> UseDestinationValue
> ExplicitExpansion

IMO的问题是当前的实现无法确定是否明确设置了bool属性.当然,通过用显式bool替换auto属性可以很容易地解决它?支持字段和默认值逻辑(以及其他流畅的配置方法,以便在基类配置中打开它时将其关闭).不幸的是,它只能在源代码中完成,因此我建议您将问题报告给他们的问题跟踪器.

直到(以及如果)他们修复它,我可以建议将所有常用代码移动到自定义扩展方法,如

static class MyMappers
{
    public static IMappingExpression<TSource,TDestination> target)
        where TSource : Source
        where TDestination : DtoBase
    {
        return target
            .ForMember(dto => dto.Name,conf =>
            {
                conf.MapFrom(src => src.Name);
                conf.ExplicitExpansion();
            });
    }
}

并从主配置代码中使用它们:

Mapper.Initialize(cfg =>
{
    cfg.CreateMap<Source,DtoBase>()
        .Configure();

    cfg.CreateMap<Source,DtoDerived>()
        .Configure()
        .ForMember(dto => dto.Desc,conf => {
            conf.MapFrom(src => src.Desc);
            conf.ExplicitExpansion();
        });
});

编辑:关于其他问题.两者都是更严重的AM处理错误,与配置无关.

问题是他们尝试使用MemberInfo实例比较来过滤投影.

第一种情况(带表达式)对于值类型失败,因为尝试从Expression< Func< T,object>>中提取MemberInfo的实现我只期待MemberExpression,但是在值类型的情况下,它包含在Expression.Convert中.

第二种情况(带有属性名称)失败,因为他们没有考虑这样的事实:从编译时lambda表达式中提取的基类继承的属性的MemberInfo与反射或运行时创建的表达式检索的属性不同,这与以下测试:

// From reflection
var nameA = typeof(DtoDerived).GetMember(nameof(DtoDerived.Name)).Single();
// Same as
//var nameA = typeof(DtoDerived).GetProperty(nameof(DtoDerived.Name));

// From compile time expression
Expression<Func<DtoDerived,NameDtoType>> compileTimeExpr = _ => _.Name;
var nameB = ((MemberExpression)compileTimeExpr.Body).Member;

// From runtime expression
var runTimeExpr = Expression.PropertyOrField(Expression.Parameter(typeof(DtoDerived)),nameof(DtoDerived.Name));
var nameC = runTimeExpr.Member;

Assert.AreEqual(nameA,nameC); // Success
Assert.AreEqual(nameA,nameB); // Fail

你肯定需要报告这两个问题.我会说,在提供表达式列表时,任何值类型属性都会泄露该功能,并且在提供名称时,任何继承的属性都会泄露.

c# – 如果在Base DTO映射中配置了ExplicitExpansion,Automapper 5.2将忽略它的更多相关文章

  1. 如何在iOS中为预期的assert / assertionFailure编写单元测试?

    这里的问题是,当someString参数为空字符串时,您可以保证函数不会失败–在您的实际应用程序中.这是因为断言不在发布版本中运行.结果是你可以在开发过程中使用assert作为调试的一种形式,但如果这种情况在现实生活中发生,你应该按顺序处理它,而不是崩溃.因此测试断言“发生”是否真的不是一种有效的单元测试技术,这就是为什么你以这种方式使用它的麻烦.

  2. ios – 核心数据并发调试:假阳性

    如WWDC2014session225(核心数据新增内容)所述,iOS8和OSX优胜美地的核心数据现在支持命令行参数-com.apple.CoreData.ConcurrencyDebug1,以启用检测到违反CoreData的并发合同的断言.在我的实验中,我发现它在iOS8beta1(在设备和模拟器中)都可以工作,但是我似乎发现了一个错误的肯定,即框架是抛出一个多线程违规异常不这样做至少这是我的信

  3. swift 断言(assert)

    和OC断言用法一样,只不过方法名去掉"NS"就可以了.断言可以让你程序在不满足断言条件时崩溃到断言处.当你的程序对参数有严格要求时,可以使用断言对参数进行检查.而且断言可以在release版本中配置去掉.

  4. Swift入门四——可选类型Optionals与断言Assert

    String和Int类型对应的可选类型分别是String?等可选类型的变量值均可以为nil。然而强制解封一个值为nil的可选类型会导致运行时错误。可选绑定Swift是一门简洁的语言,使用强制类型解封来获取可选类型变量里的值过于麻烦,是很多人不愿意使用的。所以在使用隐式可选类型变量时,为了安全起见,还是应该用if语句进行判断。附录查看完整专栏——《Swift轻松入门》

  5. 2.Swift 中的assert

    在Swift中,自带了一个断言的方法,其中的断言可选为带断言信息,有的可选为不带断言信息,下面让我们来看看.下面让我们来看看一个简单的小例子PS:assert分为两个参数,一个是condition,为bool值,为true不会断言,为false就会断言,后面的message是一个可选的错误信息,如果需要的话,就可以使用,不需要的也可以直接不写.再者,我们可以把判断条件放在condition里面去,

  6. Swift可选绑定、断言

    可选绑定可以用在if和while语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。断言可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。此外,断言允许你附加一条调试信息。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。

  7. Swift中assert(断言)的使用

    在Swift中,通过assert实现断言,assert可以帮助开发者比较容易的发现和定位错误一个断言断定条件是true.通过声明一个断言来确保某个必要的条件是满足的,以便继续执行接下来的代码。代码注意点断言可以引发程序终止,并不是被设计用来避免不在发生无效情况的。

  8. Swift 断言 assert

    断言Optionals使得我们可以检测值是否存在。在这些情况下,可以使用触发断言来终止执行并提供调试。断言是在运行时检测条件是否为true,如果为true,就继续往下执行,否则就在这里中断。

  9. 使用 Swift语言进行 Hadoop 数据流应用程序开发

    如果您发现了问题,或者希望为改进本文提供意见和建议,请在这里指出.在您开始之前,请参阅目前待解决的问题清单.简介本项目包括两类Hadoop流处理应用程序:映射器mapper和总结器reducer。如上所示,在Hadoop上编写流处理程序是一个很简单的工作,也不需要依赖于特定的软件体系。

  10. swift3 assert断言

    在Swift中,通过assert实现断言,assert可以帮助开发者比较容易的发现和定位错误。一个断言断定条件是true.通过声明一个断言来确保某个必要的条件是满足的,以便继续执行接下来的代码。例:如果断言被触发,将会强制结束程序,并打印相关信息:assertionFailed:number不大于3:file/Users/mac/Desktop/test/test/ViewController.swift,line17注意点:断言可以引发程序终止,并不是被设计用来避免不在发生无效情况的。

随机推荐

  1. c# – (wpf)Application.Current.Resources vs FindResource

    所以,我正在使用C#中的WPF创建一个GUI.它看起来像这样:它现在还没有完成.这两行是我尝试制作一种数据表,它们在XAML中是硬编码的.现在,我正在C#中实现添加新的水果按钮功能.我在XAML中有以下样式来控制行的背景图像应该是什么样子:因此,在代码中,我为每列col0,col1和col2创建一个图像,如果我使用以下代码,它添加了一个如下所示的新行:如你所见,它不太正确……为什么一个似乎忽略了一些属性而另一个没有?

  2. c# – 绑定DataGridTemplateColumn

    似乎我已经打了个墙,试图在DataGrid上使用DataTemplates.我想要做的是使用一个模板来显示每个单元格的两行文本.但是似乎无法以任何方式绑定列.以下代码希望显示我想做的事情.注意每个列的绑定:模板列没有这样的东西,因此,这个xaml不可能工作.我注定要将整个DataTemplate复制到每个列,只是对每个副本都有不同的约束?解决方法我不完全确定你想要做什么,但如果您需要获取整行的DataContext,可以使用RelativeSource绑定来移动视觉树.像这样:

  3. c# – 学习设计模式的资源

    最近我来到了这个设计模式的概念,并对此感到非常热情.你能建议一些帮助我深入设计模式的资源吗?

  4. c# – 是否有支持嵌入HTML页面的跨操作系统GUI框架?

    我想开发一个桌面应用程序来使用跨系统,是否有一个GUI框架,允许我为所有3个平台编写一次代码,并具有完全可脚本化的嵌入式Web组件?我需要它有一个API来在应用程序和网页之间进行交流.我知道C#,JavaScript和一些python.解决方法Qt有这样的事情QWebView.

  5. c# – 通过字符串在对象图中查找属性

    我试图使用任意字符串访问嵌套类结构的各个部分.给出以下(设计的)类:我想要从Person对象的一个实例的“PersonsAddress.HousePhone.Number”获取对象.目前我正在使用反思来做一些简单的递归查找,但是我希望有一些忍者有更好的想法.作为参考,这里是我开发的(crappy)方法:解决方法您可以简单地使用标准的.NETDataBinder.EvalMethod,像这样:

  6. c# – 文件下载后更新页面

    FamilyID=0a391abd-25c1-4fc0-919f-b21f31ab88b7&displaylang=en&pf=true它呈现该页面,然后使用以下元刷新标签来实际向用户提供要下载的文件:你可能需要在你的应用程序中做类似的事情.但是,如果您真的有兴趣在文件完全下载后执行某些操作,那么您的运气不佳,因为没有任何事件可以与浏览器进行通信.执行此操作的唯一方法是上传附件时使用的AJAXupload.

  7. c# – 如何在每个机器应用程序中实现单个实例?

    我必须限制我的.net4WPF应用程序,以便每台机器只能运行一次.请注意,我说每个机器,而不是每个会话.我使用一个简单的互斥体实现单实例应用程序,直到现在,但不幸的是,这样一个互斥是每个会话.有没有办法创建机器互连,还是有其他解决方案来实现每个机器应用程序的单个实例?

  8. c# – WCF和多个主机头

    我的雇主网站有多个主机名,都是同一个服务器,我们只是显示不同的皮肤来进行品牌宣传.不幸的是,在这种情况下,WCF似乎不能很好地工作.我试过overridingthedefaulthostwithacustomhostfactory.这不是一个可以接受的解决方案,因为它需要从所有主机工作,而不仅仅是1.我也看过thisblogpost,但是我无法让它工作,或者不是为了解决我的问题.我得到的错误是“这

  9. c# – ASP.NET MVC模型绑定与表单元素名称中的虚线

    我一直在搜索互联网,试图找到一种方式来容纳我的表单元素的破折号到ASP.NET的控制器在MVC2,3或甚至4中的默认模型绑定行为.作为一名前端开发人员,我更喜欢在我的CSS中使用camelCase或下划线进行破折号.在我的标记中,我想要做的是这样的:在控制器中,我会传入一个C#对象,看起来像这样:有没有办法通过一些正则表达式或其他行为来扩展Controller类来适应这种情况?我讨厌这样的事实,我必须这样做:甚至这个:思考?

  10. c# – 用户界面设计工具

    我正在寻找一个用户界面设计工具来显示文档中可能的GUI.我不能生成代码.我知道MicrosoftVisio提供了一个功能.但有什么办法吗?您使用哪种软件可视化GUI?

返回
顶部