在兩個不一樣的類型對象之間傳輸數據,一般咱們會用DTOs(數據傳輸對象),AutoMapper就是將一個對象自動轉換爲另外一個對象的技術html
一些orm框架,在用到Entity的時候有一些開源代碼用到了automapper(如:nopcommence),將數據對象轉成DTO。好比在ORM中,與數據庫交互用的Model模型是具備不少屬性變量方法神馬的。而當咱們與其它系統(或系統中的其它結構)進行數據交互時,出於耦合性考慮或者安全性考慮或者性能考慮(總之就是各類考慮),咱們不但願直接將這個Model模型傳遞給它們,這時咱們會建立一個貧血模型來保存數據並傳遞。什麼是貧血模型?貧血模型(DTO,Data Transfer Object)就是說只包含屬性什麼的,只能保存必須的數據,沒有其它任何的多餘的方法數據什麼的,專門用於數據傳遞用的類型對象。在這個建立的過程當中,若是咱們手動來進行,就會看到這樣的代碼:前端
A a=new A();
a.X1=b.X1;
a.X2=b.X2;
...
...
...
return a; 太麻煩數據庫
此時,AutoMapper能夠發揮的做用就是根據A的模型和B的模型中的定義,自動將A模型映射爲一個全新的B模型。(不用一個屬性一個屬性的賦值)後端
好處:安全
一、 db或者模型 增長字段時,只需在DTO內部增長映射,賦值代碼無需修改app
二、隔離,前端收集各參數,不用管後端定義的模型。先後端才用AutoMapper來作轉換。框架
Nuget引用:AutoMapper 版本不同,裏面的不少方法有些不同ide
AutoMapper是基於約定的,所以在實用映射以前,咱們須要先進行映射規則的配置。工具
咱們要作的只是將要映射的兩個類型告訴AutoMapper(調用Mapper類的Static方法CreateMap並傳入要映射的類型): 性能
Mapper.Initialize(cfg => { cfg.CreateMap<StudentEntity, StudentOutput>(); });
也能夠將實體類 放在配置文件MapperProfile中
Mapper.Initialize(cfg => {
cfg.AddProfile<MapperProfile>();
cfg.AddProfile<ProxyAdapterProfile>(); //可增長多個
});
注意:屢次調用 Mapper.Initialize() 只有最後一次生效。因此只能用一個Mapper.Initialize。
【AutoMapper.7.0.1】
class MapperProfile : Profile { public MapperProfile() { CreateMap<StudentEntity, StudentOutput>(); var map = CreateMap<UploadResponseBase, UploadResult>(); //字段名稱不一致,一次直接定義好全部字段的映射規則 map.ConvertUsing(s => new UploadResult { IsSuccess = s.success, FileUrl = s.clientUrl, ErrorMessage = s.rawFileName , datetimeStr = (s.datetime).ToString(), }); } }
【AutoMapper 4.2.1.0】
AutoMapper使用ForMember來指定每個字段的映射規則:
protected override void Configure() { var mapResponst = CreateMap<Response, CResponse>(); mapResponst.ForMember(dest => dest.departure_date, opt => opt.MapFrom(src => src.DepartureDate.ToString("yyyy-MM-dd HH:mm:ss"))) .ForMember(dest => dest.ticket_price, opt => opt.MapFrom(src => src.TicketPrice)); var mapContacts = CreateMap<CContacts, PassengerInputEntity>(); mapContacts.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.First_Name)) .ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.Last_Name)) .ForMember(dest => dest.AreaCode, opt => opt.MapFrom(src => src.Area_Code)); }
而後就能夠交給AutoMapper幫咱們搞定一切了:
//實例化實體List
List<StudentEntity> StudentList = new List<StudentEntity>();
//模擬數據
StudentList.Add(new StudentEntity
{
Id = 1,
Age = 12,
Gander = "boy",
Name = "WangZeLing",
Say = "Only the paranoid survive",
Score = 99M
});
//AuotMapper具體使用方法 將List<StudentEntity>轉換爲List<StudentOutput>
List<StudentOutput> Output = Mapper.Map<List<StudentOutput>>(StudentList);
Output.ForEach(output => Console.WriteLine(string.Format("name:{0},say:{1},score:{2}", output.Name, output.Say, output.Score)));
解釋
一、 AutoMapper給咱們提供的Convention或Configuration方式並非「異或的」,咱們能夠結合使用兩種方式,爲名稱不一樣的字段配置映射規則,而對於名稱相同的字段則忽略配置。
二、 在映射具備相同字段名的類型時,會自動轉換
三、 不相同名稱的屬性則須要 指定映射字段,設置ConvertUsing或者ForMember..
四、 值爲空的屬性,AutoMapper在映射的時候會把相應屬性也置爲空
五、 若是傳入一個空的AddressDto,AutoMapper也會幫咱們獲得一個空的Address對象。
Address address = Mapper.Map<AddressDto,Address>(null);
六、不須要映射的屬性能夠用Ignore忽略。【if有驗證 目標類中的全部屬性是否都被映射 時】
使用Ignore方法:
Mapper.CreateMap<Entity.Source, Entity.Destination>()
.ForMember(dest => dest.SomeValuefff, opt =>
{
opt.Ignore();
});
這段內容將討論AutoMapper的規則寫在什麼地方的問題。
在上一段中,咱們已經知道了如何使用AutoMapper進行簡單的對象映射,可是,在實際的項目中,咱們會有不少類進行映射(從Entity轉換爲Dto,或者從Entity轉換爲ViewModel等),這麼多的映射如何組織將成爲一個問題。
首先咱們須要定義一個Configuration.cs的類,該類提供AutoMapper規則配置的入口,它只提供一個靜態的方法,在程序第一次運行的時候調用該方法完成配置。
當有多個Profile的時候,咱們能夠這樣添加:
public class Configuration
{
public static void Configure()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<Profiles.SourceProfile>();
cfg.AddProfile<Profiles.OrderProfile>();
cfg.AddProfile<Profiles.CalendarEventProfile>();
});
}
}
在程序運行的時候,只須要調用Configure方法便可。
瞭解了這些實現之後,咱們能夠再項目中添加AutoMapper文件夾。
Configuration爲咱們的靜態配置入口類;Profiles文件夾爲咱們全部Profile類的文件夾。若是是MVC,咱們須要在Global中調用:
AutoMapper.Configuration.Configure();
重現:本地調試直接打開出錯的頁面,調試發現是ok的;而後先打開用到了mapper所在控制器對應的頁面,再去打開出錯的頁面,是報錯的。
從 GitHub 上籤出 AutoMapper 的源代碼一看 Mapper.Initialize() 的實現,恍然大悟。
public static void Initialize(Action<IMapperConfigurationExpression> config)
{
Configuration = new MapperConfiguration(config);
Instance = new Mapper(Configuration);
}
原來每次調用 Mapper.Initialize() 都會建立新的 Mapper 實例,也就是屢次調用 Mapper.Initialize() 只有最後一次生效。
切記不要多處調用Mapper.Initialize()。
優化方法:【寫一個工具類】
需程序集:AutoMapper
/// <summary> /// 優化AutoMap映射工具,解決AutoMap只能Initialize一次的問題 /// </summary> public class AutoMapperManager { /// <summary> /// 存儲全部的profile /// </summary> static ConcurrentBag<Profile> Profiles; static AutoMapperManager() { Profiles = new ConcurrentBag<Profile>(); } /// <summary> /// 新增Profile,必須放在靜態構造函數裏 /// </summary> /// <param name="profile"></param> public static void AddProfile(Profile profile) { Profiles.Add(profile); } /// <summary> /// 初始化,能夠屢次調用,同時以前的Profile也會生效 /// </summary> public static void Initialize() { Mapper.Initialize(config => { Profiles.ToList().ForEach(file => { config.AddProfile(file); }); }); } }
其餘地方須要用mapper的地方 調用方式:
AutoMapperManager.AddProfile(new Profile1());
AutoMapperManager.AddProfile(new Profile2());
AutoMapperManager.Initialize();
參考:
http://www.javashuo.com/article/p-webbctkb-hd.html