使用AutoMapper来展平嵌套对象的更好方法是什么?

我已经将域对象展平为DTO,如下例所示:

public class Root { public string AParentProperty { get; set; } public Nested TheNestedClass { get; set; } } public class Nested { public string ANestedProperty { get; set; } } public class Flattened { public string AParentProperty { get; set; } public string ANestedProperty { get; set; } } // I put the equivalent of the following in a profile, configured at application start // as suggested by others: Mapper.CreateMap() .ForMember ( dest => dest.ANestedProperty , opt => opt.MapFrom(src => src.TheNestedClass.ANestedProperty) ); // This is in my controller: Flattened myFlattened = Mapper.Map(myRoot); 

我已经看了很多例子,到目前为止,这似乎是扁平嵌套层次结构的方法。 但是,如果子对象具有许多属性,则此方法不会节省太多编码。

我找到了这个例子:

http://consultingblogs.emc.com/owainwragg/archive/2010/12/22/automapper-mapping-from-multiple-objects.aspx

但它需要Map()函数所需的映射对象的实例,根据我的理解,它不适用于配置文件。

我是AutoMapper的新手,所以我想知道是否有更好的方法来做到这一点。

在最新版本的AutoMapper中,您可以使用一种命名约定来避免多个.ForMember语句。

在您的示例中,如果您将Flattened类更新为:

 public class Flattened { public string AParentProperty { get; set; } public string TheNestedClassANestedProperty { get; set; } } 

您可以避免使用ForMember语句:

 Mapper.CreateMap(); 

Automapper将(根据惯例)将Root.TheNestedClass.ANestedProperty映射到Flattened.TheNestedClassANestedProperty在这种情况下。 当你使用真正的class级名字时,它看起来不那么难看,老实说!

2个可能的解决方案:

 Mapper.CreateMap() .ForMember(s=>s.AParentProperty, o=>o.Ignore()); Mapper.CreateMap() .ForMember(d => d.ANestedProperty, o => o.MapFrom(s => s.TheNestedClass)); 

另一种方法是下面的方法,但它不会传递Mapper.AssertConfigurationIsValid()

 Mapper.CreateMap() //.ForMember map your properties here Mapper.CreateMap() //.ForMember... map you properties here .AfterMap((s, d) => Mapper.Map(s.TheNestedClass, d)); 

我写了扩展方法来解决类似的问题:

 public static IMappingExpression FlattenNested( this IMappingExpression expression, Expression> nestedSelector, IMappingExpression nestedMappingExpression) { var dstProperties = typeof(TDestination).GetProperties().Select(p => p.Name); var flattenedMappings = nestedMappingExpression.TypeMap.GetPropertyMaps() .Where(pm => pm.IsMapped() && !pm.IsIgnored()) .ToDictionary(pm => pm.DestinationProperty.Name, pm => Expression.Lambda( Expression.MakeMemberAccess(nestedSelector.Body, pm.SourceMember), nestedSelector.Parameters[0])); foreach (var property in dstProperties) { if (!flattenedMappings.ContainsKey(property)) continue; expression.ForMember(property, opt => opt.MapFrom((dynamic)flattenedMappings[property])); } return expression; } 

所以在你的情况下它可以像这样使用:

 var nestedMap = Mapper.CreateMap() .IgnoreAllNonExisting(); Mapper.CreateMap() .FlattenNested(s => s.TheNestedClass, nestedMap); 

IgnoreAllNonExisting()来自这里 。

虽然它不是通用解决方案,但对于简单的情况应该足够了。

所以,

  1. 您无需在目标属性中遵循展平约定
  2. Mapper.AssertConfigurationIsValid()将通过
  3. 您也可以在非静态API(配置文件)中使用此方法

要改进另一个答案, MemberList.Source为两个映射指定MemberList.Source并设置要忽略的嵌套属性。 然后validation通过OK。

 Mapper.Initialize(cfg => { cfg.CreateMap(MemberList.Source); cfg.CreateMap(MemberList.Source) .ForSourceMember(s => s.Nested, x => x.Ignore()) .AfterMap((s, d) => Mapper.Map(s.Nested, d)); }); Mapper.AssertConfigurationIsValid(); var dest = Mapper.Map(src); 

不确定这是否会为以前的解决方案增加价值,但您可以将其作为两步映射来实现。 如果父级和子级之间存在命名冲突(最后一次胜利),请小心按正确顺序映射。

  Mapper.CreateMap(); Mapper.CreateMap(); var flattened = new Flattened(); Mapper.Map(root, flattened); Mapper.Map(root.TheNestedClass, flattened);