AutoMapper 依赖注入(DI)完全指南:AddAutoMapper、服务注册与低层 API 详解
2026/9/24 16:23:08 网站建设 项目流程

AutoMapper 依赖注入(DI)完全指南:AddAutoMapper、服务注册与低层 API 详解

【免费下载链接】AutoMapperA convention-based object-object mapper in .NET.项目地址: https://gitcode.com/gh_mirrors/au/AutoMapper

导读

本文基于 AutoMapper 官方文档的 Dependency Injection 章节,结合当前仓库源码(src/AutoMapper/ServiceCollectionExtensions.cssrc/AutoMapper/Configuration/MapperConfigurationExpression.cs等),系统讲解如何在 ASP.NET Core 中通过AddAutoMapper完成配置装配与依赖注入,包括程序集扫描、Profile 注册、IMapper/IConfigurationProvider的生命周期管理、八类可自动注册的服务类型,以及不依赖容器的低层ConstructServicesUsingAPI。读完本文,你将能在自己的 .NET 项目中熟练接入 AutoMapper 的 DI 机制,并理解其内部注册逻辑与使用边界。

一、版本背景:13.0 之后 DI 支持内置于核心包

13.0 版本开始,AddAutoMapper扩展方法已经成为 AutoMapper 核心包(AutoMapper)的一部分,原先独立的AutoMapper.Extensions.Microsoft.DependencyInjection包已被停止维护、不再发布。这一点在仓库的 13.0-Upgrade-Guide.md 中有明确记载:"AddAutoMapperis part of the core package and the DI package is discontinued"。

因此,现代项目中你只需要引用核心包,即可直接使用Microsoft.Extensions.DependencyInjection命名空间下的AddAutoMapper扩展方法。仓库中的实现位于 ServiceCollectionExtensions.cs,其命名空间正是Microsoft.Extensions.DependencyInjection,意味着它能无缝融入标准 ASP.NET Core 的容器体系。

二、ASP.NET Core 中的标准用法

2.1 用 Profile 定义配置

AutoMapper 的映射配置推荐以 Profile(配置文件类)为单位组织。每个 Profile 继承自AutoMapper.Profile,并在构造函数(或Configure方法)中调用CreateMap等 API 声明映射规则。关于 Profile 的完整说明,参见文档 Configuration.md 中的 Profile Instances 一节。仓库中 Profile.cs 的源码展示了 Profile 的核心结构:它持有TypeMapConfigs(映射配置集合),并通过CreateMap<TSource, TDestination>()CreateProjection<TSource, TDestination>()等方法将映射表达式加入集合。

2.2 在启动时调用 AddAutoMapper

Startup.ConfigureServices(或 .NET 6+ 的Program.cs中)调用IServiceCollection的扩展方法AddAutoMapper,并告知 AutoMapper 在哪些程序集中查找 Profile:

services.AddAutoMapper(cfg => { }, profileAssembly1, profileAssembly2 /*, ...*/);

其中profileAssembly1profileAssembly2System.Reflection.Assembly实例,也可以使用marker type(标记类型)写法,由 AutoMapper 自动推导其所在的程序集:

services.AddAutoMapper(cfg => { }, typeof(ProfileTypeFromAssembly1), typeof(ProfileTypeFromAssembly2) /*, ...*/);

两种写法在源码中都有对应的重载:ServiceCollectionExtensions.cs 提供了params Assembly[]params Type[]两组入口;其中Type[]重载通过t.GetTypeInfo().Assembly将标记类型转换为程序集,再交给统一的内部方法处理。

cfg参数是一个Action<IMapperConfigurationExpression>委托,可以在这里直接补充额外的全局配置(例如cfg.CreateMap<Source, Destination>()cfg.AddProfile<TProfile>()等)。仓库还额外提供了一个接收Action<IServiceProvider, IMapperConfigurationExpression>的重载,让你在配置阶段就能从容器解析服务。

2.3 注入并消费 IMapper

完成注册后,即可在任意服务或控制器中注入IMapper并在运行时执行映射:

public class EmployeesController { private readonly IMapper _mapper; public EmployeesController(IMapper mapper) => _mapper = mapper; // 使用 _mapper.Map 或 _mapper.ProjectTo }

IMapper接口定义在 Mapper.cs 中,提供了Map<TSource, TDestination>Map<TDestination>(object source)、带Action<IMappingOperationOptions>的重载等一系列映射入口。

三、AddAutoMapper 的注册机制:生命周期与装配细节

结合 ServiceCollectionExtensions.cs 的AddAutoMapperClasses私有方法,可以梳理出AddAutoMapper内部实际完成的四件事:

  1. 收集配置:若传入configAction,将其通过services.AddOptions<MapperConfigurationExpression>().Configure<IServiceProvider>(...)注册为IOptions配置源;若传入程序集,则调用options.AddMaps(assembliesToScan)扫描这些程序集中的 Profile 与[AutoMap]特性标注的类(参见 MapperConfigurationExpression.cs 中 AddMaps 的说明)。
  2. 过滤并注册服务类:程序集会先过滤掉动态程序集和 AutoMapper 自身的程序集(a.IsDynamic && a != typeof(Mapper).Assembly),再筛选出实现了 AutoMapper 服务接口的具体类,逐个用TryAddTransient注册(TryAdd语义可避免重复注册)。
  3. 注册 IConfigurationProvider 为单例(Singleton):工厂方法从容器解析IOptions<MapperConfigurationExpression>ILoggerFactory,构造MapperConfiguration。这与 MapperConfiguration.cs 中MapperConfiguration(MapperConfigurationExpression, ILoggerFactory)的构造函数一一对应——IConfigurationProvider持有全部编译好的执行计划,天然适合全局复用。
  4. 注册 IMapperIMapper的默认生命周期是Transient,且通过sp.GetService(容器解析)作为其服务构造器;IMapper的注册还带有一个防重复检查(if (services.Any(sd => sd.ServiceType == typeof(IMapper))) return services;),保证重复调用AddAutoMapper不会产生双份注册。

3.1 自定义 IMapper 生命周期

IMapper的默认生命周期为Transient(每次解析都新建实例),该默认值定义在 MapperConfigurationExpression.cs(public ServiceLifetime ServiceLifetime { get; set; } = ServiceLifetime.Transient;)。你也可以通过带ServiceLifetime参数的重载改成SingletonScoped

services.AddAutoMapper(cfg => { }, assemblies, ServiceLifetime.Singleton);

仓库测试 ServiceLifetimeTests.cs 对此做了验证:默认调用AddAutoMapper(_ => { })后,IMapperServiceDescriptor.LifetimeTransient;传入ServiceLifetime.Singleton后则变为Singleton

注意:IConfigurationProvider始终是Singleton,只有IMapper的生命周期可配置。由于IMapper内部持有IConfigurationProvider,即使IMapper是 Transient,其执行计划仍被全局缓存复用。测试 Integrations/ServiceLifetimeTests.cs 还演示了把注入IMapper的服务注册为单例的完整用法。

四、Autofac 及其他 DI 容器

  • Autofac:社区提供了第三方包AutoMapper.Contrib.Autofac.DependencyInjection可供尝试。
  • 其他 DI 引擎:AutoMapper 官方维护过一个 DI 示例合集(AutoMapper/wiki/DI-examples),涵盖多种容器的接入方式。

对于不在上述包支持范围内的容器,核心思路是使用下一节介绍的低层 API 手动接线。

五、低层 API:ConstructServicesUsing 与静态服务定位

不依赖AddAutoMapper时,AutoMapper 允许为自定义值解析器(Custom Value Resolvers)、自定义类型转换器(Custom Type Converters)、值转换器(Value Converters)和基于类的条件(Class-based Conditions)通过静态服务定位的方式提供构造函数依赖:

var configuration = new MapperConfiguration(cfg => { cfg.ConstructServicesUsing(ObjectFactory.GetInstance); cfg.CreateMap<Source, Destination>(); }, loggerFactory);

ConstructServicesUsing接收一个Func<Type, object>工厂委托,用于在映射过程中创建上述服务类型实例。该 API 有三层使用位置,作用范围依次收窄:

  1. 配置级IMapperConfigurationExpression.ConstructServicesUsing(MapperConfigurationExpression.cs),默认实现是Activator.CreateInstance(见该文件第 104 行_serviceCtor的初值),即未配置时通过反射无参构造。
  2. Mapper 实例级MapperConfiguration.CreateMapper(Func<Type, object> serviceCtor)(MapperConfiguration.cs)可为特定 Mapper 实例指定服务工厂。
  3. 单次映射级IMappingOperationOptions.ConstructServicesUsing(IMappingOperationOptions.cs),用于"子容器/嵌套容器"场景。其实现会把新工厂与原工厂做回退串联ServiceCtor = t => constructor(t) ?? ctor(t)(见该文件第 58-62 行),即先尝试新工厂,失败(返回 null)时回退到上层工厂。

使用ConstructServicesUsing时,值解析器/转换器上的构造函数参数将由该工厂按Type解析后注入。这是把 AutoMapper 手动桥接到任意 IoC 容器的标准方式,也是AddAutoMapper内部机制的底层形态。

六、自动类注册:八类服务接口一览

调用AddAutoMapper并指定程序集后,AutoMapper 会从这些程序集中自动发现以下接口的实现类,并以ServiceLifetime.Transient注册到容器:

接口用途
IValueResolver<TSource, TDestination, TDestMember>自定义成员值解析器
IMemberValueResolver<TSource, TDestination, TSourceMember, TDestMember>带源成员参数的自定义值解析器
ITypeConverter<TSource, TDestination>自定义类型转换器
IValueConverter<TSourceMember, TDestinationMember>值转换器
IDestinationFactory<TSource, TDestination>目标对象工厂
ICondition<TSource, TDestination, TMember>基于类的条件判断
IPreCondition<TSource, TDestination>映射前置条件
IMappingAction<TSource, TDestination>映射动作(Before/After Map)

源码中的判定逻辑一目了然:ServiceCollectionExtensions内维护了一个开放泛型数组AmTypes = [typeof(IValueResolver<,,>), typeof(IMemberValueResolver<,,,>), typeof(ITypeConverter<,>), typeof(IValueConverter<,>), typeof(IDestinationFactory<,,>), typeof(ICondition<,,>), typeof(IPreCondition<,>), typeof(IMappingAction<,>)](ServiceCollectionExtensions.cs),随后对每个扫描到的具体类执行type.GetGenericInterface(openType) != null判断(第 88 行),命中的即调用services.TryAddTransient(type)注册。也就是说,只要你的类实现了上述任一接口,无需任何额外注册代码,容器就能在映射时将其解析注入。

需要注意:这些服务接口的判定依赖类型恰好实现对应的开放泛型接口。如果你的实现类还实现了其他接口(如IDisposable),并不影响注册;但如果同时实现多个 AutoMapper 服务接口,它们会被分别注册。

七、Queryable Extensions:ProjectTo 与 DI 的边界

从 8.0 版本开始,可以调用IMapper.ProjectTo进行查询投影;更早的版本则需要把配置对象传给扩展方法IQueryable.ProjectTo<T>(IConfigurationProvider)

必须特别强调的是,ProjectToMap能力更受限:投影是在 LINQ 提供程序(如 EF Core 翻译成 SQL)层面完成的,只支持底层提供程序允许的映射选项(详细说明见 Queryable-Extensions.md 的 Supported Mapping Options 一节)。因此:

  • ProjectTo场景下,不能像Map那样使用依赖注入的值解析器和转换器——因为查询翻译发生在数据库端,无法执行任意 C# 服务代码。
  • 如果确实需要服务参与计算,应退回到内存映射(Map),或在投影前先把数据取回内存。

这一点也解释了为何IMapper.ProjectTo只依赖IConfigurationProvider即可工作:投影构建只读取映射配置,不触达服务容器。IConfigurationProvider可通过容器直接解析,也可通过IMapper.ConfigurationProvider属性获取(见 ServiceCollectionExtensions.cs 的注释说明)。

八、实战建议与常见坑

  1. 程序集扫描的过滤规则AddAutoMapper会自动跳过动态程序集和 AutoMapper 自身程序集(a != typeof(Mapper).Assembly),避免把 AutoMapper 内部类型误注册,无需你手动处理。
  2. 重复调用 AddAutoMapper 是安全的:服务类注册使用TryAddTransientIMapper注册有防重复检查,多次调用不会产生重复注册或异常。
  3. IMapper 生命周期与作用域的匹配:默认Transient最安全,适用于大多数 Web 场景;若改为Singleton,需注意IMapper中不应捕获 scoped 服务。仓库测试已验证单例服务注入IMapper后仍可在作用域内正常映射。
  4. 低层 API 与容器二选一:使用AddAutoMapper时无需(也不应)再手动ConstructServicesUsing;二者是"容器集成"与"手工接线"的两种方案,混用可能造成解析行为不一致。
  5. ProjectTo 与 DI 不兼容:投影查询中不要依赖值解析器/转换器的容器注入,改用可在 LINQ 表达式树中表达的映射方式。

结语

AutoMapper 的 DI 支持在 13.0 之后彻底融入核心包,AddAutoMapper承担了"程序集扫描 → Profile 装配 → 八类服务接口自动注册 →IConfigurationProvider单例 +IMapper可配置生命周期"的全部职责;ConstructServicesUsing则为非标准容器提供了等价的低层接入点。理解这两条路径及其在ProjectTo上的边界,即可在真实项目中安全、高效地使用 AutoMapper 的依赖注入能力。

【免费下载链接】AutoMapperA convention-based object-object mapper in .NET.项目地址: https://gitcode.com/gh_mirrors/au/AutoMapper

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询