咱们都知道,引用类型直接赋值传递的是地址,若是直接赋值,则改变一个类的属性的同时也在改变另外一个类。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~