我们都知道,引用类型直接赋值传递的是地址,如果直接赋值,则改变一个类的属性的同时也在改变另一个类。
所以,当我们需要实现像int类型直接赋值更改互不影响的效果时,我们需要映射。
将A类映射赋值到B类的时候,我们就需要一个对象映射器(object-object),也就是AutoMapper。
我们只需要提前配置好要映射的两个类,即可轻松实现反射。
创建一个 MapperConfiguration
实例并通过构造函数初始化配置:
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 不再可用。
组织映射配置的一个好方法是使用配置文件。创建从 Profile 继承的类,并将配置放入构造函数中:
public class OrganizationProfile : Profile{ public OrganizationProfile() { CreateMap<Foo, FooDto>(); // Use CreateMap... Etc.. here (Profile methods are the same as configuration methods) }}
在早期版本中,使用 Configure
方法而不是构造函数。从版本5开始,Configure ()
就过时了。它将在6.0版本中被删除。
配置文件中的配置只应用于配置文件中的映射。应用于根配置的配置应用于创建的所有映射。
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 }}123456789
如果你不需要变数命名原则,你可以使用精确匹配命名协议。
还可以在成员名称匹配过程中替换源成员中的单个字符或整个单词:
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:12
或者标记类型:
services.AddAutoMapper(typeof(ProfileTypeFromAssembly1), typeof(ProfileTypeFromAssembly2) /*, ...*/);1
现在你可以在运行时将 AutoMapper 注入到你的服务/控制器中:
public class EmployeesController { private readonly IMapper _mapper; public EmployeesController(IMapper mapper) => _mapper = mapper; // use _mapper.Map or _mapper.ProjectTo}
Autofac for AutoMapper
对于那些使用 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; }}
工作流程如下:
通过 myregistry.Register 注册你的类型
MapperProvider
允许您直接将 IMapper
实例注入到其他类中
使用 propertythatdependensoniovalueresolver
解析一个值
将 IService 注入到 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}
2016 © donet5.comApache Licence 2.0