AutoMapper 10.0使用教程
阅读原文时间:2023年07月09日阅读:4

这里有个目录

什么是AutoMapper

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

所以,当我们需要实现像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 继承的类,并将配置放入构造函数中:

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

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

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

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

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

从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 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在这里是一个例子

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~

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器