使用AutoMapper實作Dto和Model的自由轉換(下)

巴扎黑
發布: 2016-12-20 11:31:49
原創
1684 人瀏覽過

書接上文。在上一篇文章中我們討論了使用AutoMapper實作類型間1-1映射的兩種方式-Convention和Configuration,知道如何進行簡單的OO Mapping。在這個系列的最後一篇文章我想基於我們的需求討論一些中級別的話題,包括:如何實現類型體型之間的映射,以及如何為兩個類型實現多個映射規則。 
【四】將一個類型映射為類型體系 
先回顧一下我們的Dto和Model。我們有BookDto,我們有Author,每個Author有自己的ContactInfo。現在提一個問題:如何從BookDto得到第一個作者的Author物件?答案即簡單,又不簡單。
最簡單的做法是,使用前面提到的CountructUsing,指定BookDto到Author的全部欄位及子類型欄位的對應: 

C#程式碼  

var map 

map.ConstructUsing(s => new Author  

                                           Name = s.First AuthorName,          Description = s.FirstAuthorDescription,  

             ContactInfo  

                           

                             .FirstAuthorBlog,  

                             Email = s.FirstAuthorEmail,  

                              Twitter = s.FirstAuthorTwitter  

                          }  

                       


這樣的做法可以工作,但很不經濟。因為我們是在從頭開始做BookDto到Author的映射,而從BookDto到ContactInfo的映射是我們之前已經實現過的,但實在沒有必要重複再寫一次。設想一下,如果有一個個別的什麼Reader類型裡面也包含有ContactInfo,在做BookDto到Reader映射的時候,我們是不是再寫一遍這個BookDto -> ContactInfo邏輯呢?再設想一下如果我們在實現BookDto到Book的映射的時候,是不是又需要把BookDto到Author的映射規則再重複寫一遍呢? 
所以我認為對於這種類型體系間的映射,比較理想的做法是為每個具體類型指定簡單的映射,而後在映射複雜類型的時候再復用簡單類型的映射。用簡單點的語言描述: 
我們有A,B,C,D四個類型,其中B = [C, D]。已知A -> C, A -> D, 求A -> B。 
我的解法是使用AutoMapper提供的--IValueResolver。 IValueResolver是AutoMapper為實現字段層級的特定映射邏輯而定義的類型,它的定義如下: 

C#代碼  

public interface IValueResolver  source);  

}  

而在實際的應用中我們傾向於使用它的泛型子類別-ValueResolver,並實現它的抽象方法: 

C#程式碼  

protected abstract TD ,TDestination為目標欄位的類型。

回到我們的例子,我們現在可以這樣來映射BookDto -> Author: 

C#代碼  

var map = Mapper.CreateMap(); = Mapper.CreateMap(); Name, opt => opt.MapFrom(s => s.FirstAuthorName))  

    .ForMember(d => d.Description,opt  .ForMember(d => d.Description,1opt  .ForMember(d => d.Description,Fopt  .ForMember(d 
    .ForMember(d => d.ContactInfo,  

              opt => opt.ResolveUsing)); solver並重複使用BookDto -> ContactInfo的邏輯: 

C#代碼  

public class FirstAuthorContactInfoResolver : ValueResolver  

{  

    protected          return Mapper.Map(source);  

    }  


} 搞定了。 
類似的,我們現在也可以實現BookDto -> Book了吧?透過重複使用BookDto -> Author以及BookDto -> Publisher。 

真的可以嗎?好像還有問題。是的,我們會發現需要從BookDto映射到兩個不同的Author,它們的字段映射規則是不同的。怎麼辦?趕緊進入我們的最後一個議題。 

【五】為兩個類型實現多套映射規則 

我們的問題是:對於類型A和B,需要定義2個不同的A -> B,並讓它們可以同時使用。事實上目前的AutoMapper並沒有提供現成的方式做到這一點。 

當然我們可以採用「曲線救國」的方法-為first author和second author分別定義Author的兩個子類,比如說FirstAuthor和SecondAuthor,然後分別實作BookDto -> FirstAuthor和BookDto -> SecondAuthor映射。但是這種方法也不太經濟。假如還有第三作者甚至第四作者呢?為每一個作者都定義一個Author的子類別嗎? 

另一方面,我們不妨假設一下,如果AutoMapper提供了這樣的功能,那會是什麼樣子呢? CreateMap方法與Map方法應該這樣定義: 

C#程式碼  

CreateMap(string tag)  

用於標識該映射的標籤。 

而我們在使用的時候,就可以: 

C#代碼  

var firstAuthorMap = Mapper.CreateMap("first");

// Define BookDto -> first Author rule  

var secondAuthorMap = Mapper.CreateMap("second");Dp. rule  

var firstAuthor = Mapper.Map(source, "first");  

var secondAuthor = Mapper.Map(source, "second");  

但是沒有關係,雖然AutoMapper關上了這扇門,卻為我們留著另一扇門-MappingEngine。

MappingEngine是AutoMapper的映射執行引擎,事實上在Mapper中有預設的MappingEngine,我們在呼叫Mapper.CreateMap的時候,是往與這個預設的MappingEngine對應的Configuration中寫規則,在呼叫Mapper.Map取得物件的時候則是使用預設的MappingEngine執行其對應Configuration中的規則。
簡而言之一個MappingEngine就是一個AutoMapper的“虛擬機”,如果我們同時啟動多個“虛擬機”,並且將針對同一對類型的不同映射規則放到不同的“虛擬機”上,就可以讓它們各自相安無事的運作起來,使用的時候要用哪個規則就問相應的「虛擬機器」去要好了。 
說做就做。首先我們定義一個MappingEngineProvider類,用它來取得不同的MappingEngine: 


C#代碼  

public class MappingEngineProvider   

  

    public MappingEngine Get()  

    {  

      {  _engine;  

    }  

}  

我們將不同類型的對應規則抽象化為介面IMapping:   

    void AddTo(Configuration config);  

}  



接著在MappingEngineProvider的建構子裡將需要的規則放到對應的MappingEngine中: 

C#程式碼  

private static Dictionary  

public MappingEngineProvider(Engine engine)  

{  


     

    _rules[engine].ForEach(r=> r.AddTo (config));  

    _mappingEngine = new MappingEngine(config);  

}  

public enum Engine  

{  

    Basic = 0,  

    First,  

}用於放置所有基本的映射規則,First用於放置所有Dto -> FirstXXX的規則, Second則用來放置所有Dto -> SecondXXX的規則。 

我們也定義了一個放置所有映射規則的字典_rule,將規則分門別類別放到不同的Engine中。 

剩下的事情就是往字典_rule裡填入我們的mapping了。例如我們把BookDtoToFirstAuthorMapping放到First engine裡並把BookDtoToSecondAuthorMapping放到Second

    new Dictionary>  


        {  

            {   List  

                            

                                                              }        {  

                Engine.Sec                     {  

              new BookDtoToSecondAuthorMapping(),  

                                  },  

        };  

      };  

當然為了方便使用我們可以事先實例化好不同的MappingEngineProvider物件: 

C#程式碼  

public static SimpleMap pingEngineProvider Second = new MappingEngineProvider(Engine.Second) ;  

現在我們就可以在映射BookDto -> Book的時候同時使用這2個Engine來得到2個Author並把它們組裝到字段Book.Authors裡面了: 


🠎 DefaultMapping  

{  

    protected override void MapMembers(IMappingExpression d.Authors,  

                 >());  

    }  


  

    private class AuthorsValueRe  

        protected override List ResolveCor e(BookDto source)  

   stAuthor = SimpleMappingEngineProvider .First.Get().Map(source);  

            var secondAuthor             return firstAuthor.IsNull()  

? secondAuthor.IsNull() ? new List() : new List {new Author(), secondAuthor}  : secondAuthor.IsNull()  

                       

                             : new List  

    }  

  

}  

最後,但還記得我們在本節開始的時候提到的美好意願嗎?既然AutoMapper沒有幫我們實現,就讓我們自己實現: 



C#代碼  

public class MyMapper  

{  ines = new Dictionary  

{  

                                                {Engine.Basic, MappingEngineProvider.Basic.Get()},                                    地},  

                                            {Engine.Second, MappingEngineProvider.Second.Get()},  

                                                                   };  

  

    public static TTarget Map(TSource source, Engine engine = Engine.Basic)  

    {  

        return Engines[engine].Map(source);  

}  

}  

一切又回來了,我們可以這樣做: 

C#代碼  

var firstAuthor var secondAuthor = MyMapper. Map(dto, Engine.Second);  

也可以這樣了: 

C#代碼  

發現在家裡要上傳文件到Github真是奇慢無比,所有我決定先把自己的程式碼打包上傳,歡迎大家參考使用。


相關標籤:
c#
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板