zoukankan      html  css  js  c++  java
  • AutoMapper 10.0使用教程

    什么是AutoMapper

    我们都知道,引用类型直接赋值传递的是地址,如果直接赋值,则改变一个类的属性的同时也在改变另一个类。

    所以,当我们需要实现像int类型直接赋值更改互不影响的效果时,我们需要映射

    将A类映射赋值到B类的时候,我们就需要一个对象映射器(object-object),也就是AutoMapper。

    我们只需要提前配置好要映射的两个类,即可轻松实现反射。

    配置

    使用MapperConfiguration配置

    创建一个 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 Instances配置

    组织映射配置的一个好方法是使用配置文件。创建从 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版本中被删除。

    配置文件中的配置只应用于配置文件中的映射。应用于根配置的配置应用于创建的所有映射。

    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 继承类,并将它们添加到配置中。

    Naming Conventions(命名约定)

    您可以设置源和目标命名约定

    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
      }
    }
    

    如果你不需要变数命名原则,你可以使用精确匹配命名协议。

    Replacing characters (替换字符)

    还可以在成员名称匹配过程中替换源成员中的单个字符或整个单词:

    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");
    });
    

    Recognizing pre/postfixes 识别前/后缀

    有时候,源/目标属性会有共同的前/后缀,这导致您必须执行一系列自定义成员映射,因为名称不匹配。为了解决这个问题,您可以识别前/后缀:

    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
    

    Visibility 配置可见性

    默认情况下,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>();
    });
    

    Configuration compilation 配置编译

    由于表达式编译可能占用位资源,因此 AutoMapper 在第一个映射上编译类型映射计划。然而,这种行为并不总是可取的,所以你可以告诉 AutoMapper 直接编译它的映射:

    var configuration = new MapperConfiguration(cfg => {});
    configuration.CompileMappings();
    

    对于几百个映射,这可能需要几秒钟。

    Dependency Injection (依赖注入)

    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 });
    

    Queryable Extensions 可查询扩展

    从8.0开始,你可以使用 IMapper。ProjectTo.对于旧版本,您需要将配置传递给扩展方法 IQueryable。项目组 < t > (图像提供者)。

    注意 IQueryable。ProjectTo是比IMappe更有限 的映射,因为只支持基础 LINQ 提供程序所允许的内容。这意味着不能像对 Map 那样对值解析器和转换器使用 DI。

    例子

    ASP.NET Core

    有一个 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
    }
    

    AutoFac

    Autofac for AutoMapper

    Ninject

    对于那些使用 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;
        }
    }
    

    Simple Injector 简单注射器

    工作流程如下:

    1. 通过 myregistry.Register 注册你的类型
    2. MapperProvider 允许您直接将 IMapper 实例注入到其他类中
    3. 使用 propertythatdependensoniovalueresolver 解析一个值
    4. 将 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

    对于那些使用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

    对于那些使用 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~

  • 相关阅读:
    课程评价
    6.1-6.7 第十六周总结
    5.31 软件开发日志
    5.25-5.31 第十五周总结
    5.30 软件开发日志
    5.29 软件开发日志
    5.28 软件开发日志
    对搜狗输入法的评价
    找水王
    用户模板/用户场景
  • 原文地址:https://www.cnblogs.com/ma-nong01/p/14323416.html
Copyright © 2011-2022 走看看