2011-07-04 71 views
15

我已被平坦化的域对象插入的DTO如显示在下面的例子:使用AutoMapper压扁嵌套对象的更好方法?

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<Root, Flattened>() 
     .ForMember 
     (
      dest => dest.ANestedProperty 
      , opt => opt.MapFrom(src => src.TheNestedClass.ANestedProperty) 
     ); 

// This is in my controller: 
Flattened myFlattened = Mapper.Map<Root, Flattened>(myRoot); 

我已经看过了一些实例,且到目前为止,这似乎是一个压平嵌套层级的方式。但是,如果子对象具有多个属性,则此方法不会节省太多编码。

我发现这个例子:

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

,但它需要映射的对象,由图()函数要求,按照我的理解,这将不与轮廓工作的实例。

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

+0

我有和约翰一样的挑战。 – noocyte

+0

没有人可以帮忙吗? –

回答

10

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

在您的例子,如果你更新你的平顶类是:

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

可避免使用ForMember声明:

Mapper.CreateMap<Root, Flattened>(); 

Automapper会(按照约定)地图Root.TheNestedClass.ANestedProperty到在这种情况下为Flattened.TheNestedClassANestedProperty。当你使用真实的类名时,它看起来不那么难看,诚实!

+0

基于Automapper的使用命名我的视图模型属性并不是我想要做的。我很欣赏答案,但解决方案的副作用会让我在原始问题中使用该技术。 – Choco

3

2个可能的解决方案:

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

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

Mapper.CreateMap<Nested, Flattened>() 
//.ForMember map your properties here 
Mapper.CreateMap<Root, Flattened>() 
//.ForMember... map you properties here 
.AfterMap((s, d) => Mapper.Map(s.TheNestedClass, d)); 
+1

不错的做法;在这个配置上调用'Mapper.AssertConfigurationIsValid();'失败,出现两个错误(创建了两个地图,其中没有一个完全覆盖目标类型的属性) –

+3

不确定如果有很多字段在嵌套类?如果在应从嵌套对象映射的Flattened对象中有多个属性,则OP希望避免添加多个“For destination Member”语句。 –

+0

这只是命名约定方法的替代方案。并非所有的人都有可能一直改变/重构属性名称,以符合AutoMapper的命名约定。 –

1

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

public static IMappingExpression<TSource, TDestination> FlattenNested<TSource, TNestedSource, TDestination>(
    this IMappingExpression<TSource, TDestination> expression, 
    Expression<Func<TSource, TNestedSource>> nestedSelector, 
    IMappingExpression<TNestedSource, TDestination> 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<Nested, Flattened>() 
         .IgnoreAllNonExisting(); 

Mapper.CreateMap<Root, Flattened>() 
     .FlattenNested(s => s.TheNestedClass, nestedMap); 

IgnoreAllNonExisting()here

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

所以,

  1. 你并不需要按照扁平化公约目标的属性
  2. Mapper.AssertConfigurationIsValid()将通过
  3. 可以在非静态API使用此方法(配置文件)
+1

我无法使用最新版本的AutoMapper获得此功能。 MappingExpression.TypeMap不再可用。 –

0

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

Mapper.Initialize(cfg => 
{ 
    cfg.CreateMap<SrcNested, DestFlat>(MemberList.Source); 
    cfg.CreateMap<SrcRoot, DestFlat>(MemberList.Source) 
     .ForSourceMember(s => s.Nested, x => x.Ignore()) 
     .AfterMap((s, d) => Mapper.Map(s.Nested, d)); 
}); 

Mapper.AssertConfigurationIsValid(); 

var dest = Mapper.Map<SrcRoot, DestFlat>(src);