2011-07-04 111 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);