2014-01-28 55 views
34

轉換比方說,我有兩個模型類:AutoMapper從多個來源

public class People { 
    public string FirstName {get;set;} 
    public string LastName {get;set;} 
} 

也有一類電話:

public class Phone { 
    public string Number {get;set;} 
} 

我想轉換爲PeoplePhoneDto這樣的:

public class PeoplePhoneDto { 
    public string FirstName {get;set;} 
    public string LastName {get;set;} 
    public string PhoneNumber {get;set;} 
} 

讓我們說在我的控制器我有:

var people = repository.GetPeople(1); 
var phone = repository.GetPhone(4); 

// normally, without automapper I would made 
return new PeoplePhoneDto(people, phone) ; 

我似乎無法找到此場景的任何示例。這可能嗎 ?

注意:示例不是真實的,只是針對此問題。

+0

@Andrei雖然我同意這似乎相似,它是在它試圖解決問題的差異解決。從這個問題中很難理解它將如何應用於這個問題。 –

+0

爲什麼不讓'PeoplePhoneDto'擁有'People'和'Phone'成員? – crush

+0

因爲這不是我想要公開的。 –

回答

56

你不能直接映射許多來源單一目的地 - 你應該逐個應用映射,如Andrew Whitaker答案描述。所以,你必須定義所有映射:

Mapper.CreateMap<People, PeoplePhoneDto>(); 
Mapper.CreateMap<Phone, PeoplePhoneDto>() 
     .ForMember(d => d.PhoneNumber, a => a.MapFrom(s => s.Number)); 

然後通過這些映射的創建目標對象,並應用其他映射到創建的對象。這一步可以用非常簡單的擴展方法進行簡化:

public static TDestination Map<TSource, TDestination>(
    this TDestination destination, TSource source) 
{ 
    return Mapper.Map(source, destination); 
} 

用法很簡單:

var dto = Mapper.Map<PeoplePhoneDto>(people) 
       .Map(phone); 
+1

正是我所期待的!謝謝 ! –

+0

在AutoMapper上有一個抽象[IMapper](https://gist.github.com/ilyapalkin/8822638)將多個源映射到我使用的單個目標。 –

+0

@Sergey Berezovskiy,我創建了映射,在PeoplePhoneDto類中添加了擴展方法,並複製粘貼了您的用法(即,我複製粘貼了所需的所有內容),但是我得到「方法Map沒有重載1個參數」錯誤。我錯過了什麼?我正在使用Automapper 4.2.1。 – HeyJude

8

你可以使用這個Tuple

Mapper.CreateMap<Tuple<People, Phone>, PeoplePhoneDto>() 
    .ForMember(d => d.FirstName, opt => opt.MapFrom(s => s.Item1.FirstName)) 
    .ForMember(d => d.LastName, opt => opt.MapFrom(s => s.Item1.LastName)) 
    .ForMember(d => d.Number, opt => opt.MapFrom(s => s.Item2.Number)); 

如果你希望你可以使用不同的表示(列表,字典或別的東西),將所有這些模型聚集在一起的多個源模型資源。

上面的代碼應該優先放置在某個AutoMapperConfiguration文件中,一次全局設置,然後在適用時使用。

默認情況下,AutoMapper僅支持單個數據源。所以不可能直接設置多個數據源(沒有將數據包封裝在一個集合中),因爲那麼我們如何知道如果兩個源模型具有相同名稱的屬性?

有,雖然一些變通方法來實現這一目標:

public static class EntityMapper 
{ 
    public static T Map<T>(params object[] sources) where T : class 
    { 
     if (!sources.Any()) 
     { 
      return default(T); 
     } 

     var initialSource = sources[0]; 

     var mappingResult = Map<T>(initialSource); 

     // Now map the remaining source objects 
     if (sources.Count() > 1) 
     { 
      Map(mappingResult, sources.Skip(1).ToArray()); 
     } 

     return mappingResult; 
    } 

    private static void Map(object destination, params object[] sources) 
    { 
     if (!sources.Any()) 
     { 
      return; 
     } 

     var destinationType = destination.GetType(); 

     foreach (var source in sources) 
     { 
      var sourceType = source.GetType(); 
      Mapper.Map(source, destination, sourceType, destinationType); 
     } 
    } 

    private static T Map<T>(object source) where T : class 
    { 
     var destinationType = typeof(T); 
     var sourceType = source.GetType(); 

     var mappingResult = Mapper.Map(source, sourceType, destinationType); 

     return mappingResult as T; 
    } 
} 

然後:

var peoplePhoneDto = EntityMapper.Map<PeoplePhoneDto>(people, phone); 

但相當誠實,即使我使用AutoMapper對於已經幾年我從來沒有有需要使用來自多個來源的映射。 例如,在我單視圖模型中需要多個業務模型的情況下,我只是將這些模型嵌入到視圖模型類中。

因此,在你的情況是這樣的:

public class PeoplePhoneDto { 
    public People People { get; set; } 
    public Phone Phone { get; set; } 
} 
+1

所以我必須在做映射之前創建一個元組,我想知道什麼是automapper的真正好處......聽起來有點矯枉過正。有什麼辦法可以避免創建另一種類型(元組,dic等)? –

+0

感謝您的回答,這讓我對automapper有了很多瞭解。事情是,當你公開API的時候,你需要仔細地模擬數據,這種方式有時不需要嵌入式模型,因爲你將與'領域相關'的問題轉移給了消費者,我試圖讓客戶端不需要嵌套類型。如果這是爲了我自己的內部使用,我肯定會繼續使用嵌入式選項。 –

+1

您建議的'PeoplePhoneDto'看起來不錯,但我仍然認爲從多個來源映射是有用的,最顯着的是映射視圖模型。我認爲大多數真實世界的場景需要多個來源來構建視圖模型。我想你可以創建視圖模型來解決問題,但我認爲創建視圖模型並不關心業務模式的外觀是個好主意。 –