咱們都知道,引用類型直接賦值傳遞的是地址,若是直接賦值,則改變一個類的屬性的同時也在改變另外一個類。web
因此,當咱們須要實現像int類型直接賦值更改互不影響的效果時,咱們須要映射。微信
將A類映射賦值到B類的時候,咱們就須要一個對象映射器(object-object),也就是AutoMapper。app
咱們只須要提早配置好要映射的兩個類,便可輕鬆實現反射。ide
建立一個 MapperConfiguration
實例並經過構造函數初始化配置:svg
var config = new MapperConfiguration(cfg => { cfg.CreateMap<Foo, Bar>(); cfg.AddProfile<FooProfile>(); });
MapperConfiguration
實例能夠靜態存儲,也能夠存儲在靜態字段或依賴注入容器中。一旦建立,它就不能被更改/修改。函數
var configuration = new MapperConfiguration(cfg => { cfg.CreateMap<Foo, Bar>(); cfg.AddProfile<FooProfile>(); });
注:從9.0開始,靜態 API 再也不可用。post
組織映射配置的一個好方法是使用配置文件。建立從 Profile 繼承的類,並將配置放入構造函數中:網站
// This is the approach starting with version 5 public class OrganizationProfile : Profile { public OrganizationProfile() { CreateMap<Foo, FooDto>(); // Use CreateMap... Etc.. here (Profile methods are the same as configuration methods) } } // How it was done in 4.x - as of 5.0 this is obsolete: // public class OrganizationProfile : Profile // { // protected override void Configure() // { // CreateMap<Foo, FooDto>(); // } // }
在早期版本中,使用 Configure
方法而不是構造函數。從版本5開始,Configure ()
就過期了。它將在6.0版本中被刪除。this
配置文件中的配置只應用於配置文件中的映射。應用於根配置的配置應用於建立的全部映射。spa
Assembly Scanning for auto configuration (自動配置程序集掃描)
配置文件能夠經過多種方式直接添加到主映射器配置中:
cfg.AddProfile<OrganizationProfile>(); cfg.AddProfile(new OrganizationProfile()); or by automatically scanning for profiles:
或者經過自動掃描檔案:
// Scan for all profiles in an assembly // ... using instance approach: var config = new MapperConfiguration(cfg => { cfg.AddMaps(myAssembly); }); var configuration = new MapperConfiguration(cfg => cfg.AddMaps(myAssembly)); // Can also use assembly names: var configuration = new MapperConfiguration(cfg => cfg.AddMaps(new [] { "Foo.UI", "Foo.Core" }); ); // Or marker types for assemblies: var configuration = new MapperConfiguration(cfg => cfg.AddMaps(new [] { typeof(HomeController), typeof(Entity) }); );
AutoMapper 將掃描指定的程序集,從 Profile
繼承類,並將它們添加到配置中。
您能夠設置源和目標命名約定
var configuration = new MapperConfiguration(cfg => { cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention(); });
這將把如下屬性映射到彼此: property _ name-> PropertyName
您還能夠將其設置爲每一個配置文件級別
public class OrganizationProfile : Profile { public OrganizationProfile() { SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); DestinationMemberNamingConvention = new PascalCaseNamingConvention(); //Put your CreateMap... Etc.. here } }
若是你不須要變數命名原則,你可使用精確匹配命名協議。
還能夠在成員名稱匹配過程當中替換源成員中的單個字符或整個單詞:
public class Source { public int Value { get; set; } public int Ävíator { get; set; } public int SubAirlinaFlight { get; set; } } public class Destination { public int Value { get; set; } public int Aviator { get; set; } public int SubAirlineFlight { get; set; } }
We want to replace the individual characters, and perhaps translate a word:
咱們想要替換單個字符,或許能夠翻譯一個單詞:
var configuration = new MapperConfiguration(c => { c.ReplaceMemberName("Ä", "A"); c.ReplaceMemberName("í", "i"); c.ReplaceMemberName("Airlina", "Airline"); });
有時候,源/目標屬性會有共同的前/後綴,這致使您必須執行一系列自定義成員映射,由於名稱不匹配。爲了解決這個問題,您能夠識別前/後綴:
public class Source { public int frmValue { get; set; } public int frmValue2 { get; set; } } public class Dest { public int Value { get; set; } public int Value2 { get; set; } } var configuration = new MapperConfiguration(cfg => { cfg.RecognizePrefixes("frm"); cfg.CreateMap<Source, Dest>(); });
默認狀況下,AutoMapper 識別前綴「 Get」 ,若是您須要清除前綴:
var configuration = new MapperConfiguration(cfg => { cfg.ClearPrefixes(); cfg.RecognizePrefixes("tmp"); });
Global property/field filtering 全局屬性/字段篩選
默認狀況下,AutoMapper 會嘗試映射每一個公共屬性/字段。您可使用屬性/字段過濾器過濾出屬性/字段:
var configuration = new MapperConfiguration(cfg => { // don't map any fields cfg.ShouldMapField = fi => false; // map properties with a public or private getter cfg.ShouldMapProperty = pi => pi.GetMethod != null && (pi.GetMethod.IsPublic || pi.GetMethod.IsPrivate); }); Configuring
默認狀況下,AutoMapper 只能識別公共成員。它能夠映射到私有 setters
,可是若是整個屬性都是 private/internal
,則會跳過 internal/private
方法和屬性。要指示 AutoMapper 識別具備其餘可視性的成員,請覆蓋默認過濾器 ShouldMapField
和/或 shouldmapproty
:
var configuration = new MapperConfiguration(cfg => { // map properties with public or internal getters cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly; cfg.CreateMap<Source, Destination>(); });
因爲表達式編譯可能佔用位資源,所以 AutoMapper 在第一個映射上編譯類型映射計劃。然而,這種行爲並不老是可取的,因此你能夠告訴 AutoMapper 直接編譯它的映射:
var configuration = new MapperConfiguration(cfg => {}); configuration.CompileMappings();
對於幾百個映射,這可能須要幾秒鐘。
AutoMapper 支持使用靜態服務位置構建自定義值解析器、自定義類型轉換器和值轉換器:
var configuration = new MapperConfiguration(cfg => { cfg.ConstructServicesUsing(ObjectFactory.GetInstance); cfg.CreateMap<Source, Destination>(); });
或動態服務位置,用於基於實例的容器(包括子/嵌套容器) :
var mapper = new Mapper(configuration, childContainer.GetInstance); var dest = mapper.Map<Source, Destination>(new Source { Value = 15 });
從8.0開始,你可使用 IMapper。ProjectTo.對於舊版本,您須要將配置傳遞給擴展方法 IQueryable。項目組 < t > (圖像提供者)。
注意 IQueryable。ProjectTo
是比IMappe更有限 的映射,由於只支持基礎 LINQ 提供程序所容許的內容。這意味着不能像對 Map 那樣對值解析器和轉換器使用 DI。
有一個 NuGet 包將與這裏描述的默認注入機制一塊兒使用,並在這個項目中使用。
您可使用配置文件定義配置。而後你讓 AutoMapper 知道哪些程序集是經過在啓動時調用 IServiceCollection 擴展方法 AddAutoMapper 定義的概要文件:
services.AddAutoMapper(profileAssembly1, profileAssembly2 /*, ...*/); or marker types:
或者標記類型:
services.AddAutoMapper(typeof(ProfileTypeFromAssembly1), typeof(ProfileTypeFromAssembly2) /*, ...*/);
如今你能夠在運行時將 AutoMapper 注入到你的服務/控制器中:
public class EmployeesController { private readonly IMapper _mapper; public EmployeesController(IMapper mapper) => _mapper = mapper; // use _mapper.Map or _mapper.ProjectTo }
對於那些使用 Ninject 的人來講,這裏是一個用於 AutoMapper 的 Ninject 模塊的例子
public class AutoMapperModule : NinjectModule { public override void Load() { Bind<IValueResolver<SourceEntity, DestModel, bool>>().To<MyResolver>(); var mapperConfiguration = CreateConfiguration(); Bind<MapperConfiguration>().ToConstant(mapperConfiguration).InSingletonScope(); // This teaches Ninject how to create automapper instances say if for instance // MyResolver has a constructor with a parameter that needs to be injected Bind<IMapper>().ToMethod(ctx => new Mapper(mapperConfiguration, type => ctx.Kernel.Get(type))); } private MapperConfiguration CreateConfiguration() { var config = new MapperConfiguration(cfg => { // Add all profiles in current assembly cfg.AddMaps(GetType().Assembly); }); return config; } }
工做流程以下:
MapperProvider
容許您直接將 IMapper
實例注入到其餘類中propertythatdependensoniovalueresolver
解析一個值propertythatdependensoniovalueresolver
中,而後就可使用了ValueResolver 能夠訪問 IService,由於咱們經過 MapperConfigurationExpression. ConstructServicesUsing 註冊容器
public class MyRegistrar { public void Register(Container container) { // Injectable service container.RegisterSingleton<IService, SomeService>(); // Automapper container.RegisterSingleton(() => GetMapper(container)); } private AutoMapper.IMapper GetMapper(Container container) { var mp = container.GetInstance<MapperProvider>(); return mp.GetMapper(); } } public class MapperProvider { private readonly Container _container; public MapperProvider(Container container) { _container = container; } public IMapper GetMapper() { var mce = new MapperConfigurationExpression(); mce.ConstructServicesUsing(_container.GetInstance); mce.AddMaps(typeof(SomeProfile).Assembly); var mc = new MapperConfiguration(mce); mc.AssertConfigurationIsValid(); IMapper m = new Mapper(mc, t => _container.GetInstance(t)); return m; } } public class SomeProfile : Profile { public SomeProfile() { var map = CreateMap<MySourceType, MyDestinationType>(); map.ForMember(d => d.PropertyThatDependsOnIoc, opt => opt.MapFrom<PropertyThatDependsOnIocValueResolver>()); } } public class PropertyThatDependsOnIocValueResolver : IValueResolver<MySourceType, object, int> { private readonly IService _service; public PropertyThatDependsOnIocValueResolver(IService service) { _service = service; } int IValueResolver<MySourceType, object, int>.Resolve(MySourceType source, object destination, int destMember, ResolutionContext context) { return _service.MyMethod(source); } }
對於那些使用Castle Windsor在這裏是一個例子
public class AutoMapperInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { // Register all mapper profiles container.Register( Classes.FromAssemblyInThisApplication(GetType().Assembly) .BasedOn<Profile>().WithServiceBase()); // Register IConfigurationProvider with all registered profiles container.Register(Component.For<IConfigurationProvider>().UsingFactoryMethod(kernel => { return new MapperConfiguration(configuration => { kernel.ResolveAll<Profile>().ToList().ForEach(configuration.AddProfile); }); }).LifestyleSingleton()); // Register IMapper with registered IConfigurationProvider container.Register( Component.For<IMapper>().UsingFactoryMethod(kernel => new Mapper(kernel.Resolve<IConfigurationProvider>(), kernel.Resolve))); } }
對於那些使用 Catel.IoC 的用戶,下面介紹如何註冊自動控制器。首先使用配置文件定義配置。而後你讓 AutoMapper 知道在哪些程序集中這些配置文件是經過在啓動時在 ServiceLocator 中註冊 AutoMapper 定義的:
配置建立方法:
public static MapperConfiguration CreateConfiguration() { var config = new MapperConfiguration(cfg => { // Add all profiles in current assembly cfg.AddMaps(GetType().Assembly); }); return config; }
如今你能夠在運行時將 AutoMapper 注入到你的服務/控制器中:
public class EmployeesController { private readonly IMapper _mapper; public EmployeesController(IMapper mapper) => _mapper = mapper; // use _mapper.Map or _mapper.ProjectTo }
本人不是大佬,只是道路先行者,在落河後,向後來的人大喊一聲,這裏有坑,不要過來啊!
縱然如此,依舊有人重複着落河,重複着吶喊······
我的博客網站 Blog
技術交流Q羣: 1012481075 羣內有各類流行書籍資料
文章後續會在公衆號更新,微信搜索 OneByOneDotNet 便可關注。
你的一分鼓勵,個人十分動力,點贊免費,感恩回饋。喜歡就點贊評論吧,雙擊6666~