- 后端
【免费下载链接】AutoMapper
A convention-based object-object mapper in .NET.
本文是面向 AutoMapper 15.0 的升级实战指南,完整解读本次版本在目标框架、商业许可与 DI 集成 API 上的三项重大变化,并针对AddAutoMapper与MapperConfiguration的破坏性变更给出可复制的迁移代码。读完本文,你将能够把现有 14.x 及以下版本的项目平稳升级到 15.0,并正确配置许可证、适配新的构造签名,避免编译错误与运行时日志异常。
本指南以 docs/source/15.0-Upgrade-Guide.md 为骨架,结合仓库中 src/AutoMapper 的源码实现与 docs/source/License-configuration.md 官方文档展开,全部结论均可在此仓库内验证。
一、目标框架调整:.NET 8、.NET 9 与 .NET Standard 2.0
15.0 版本将目标框架(Target Framework)收敛为.NET 8、.NET 9 与 .NET Standard 2.0。这意味着:
- 面向 .NET 8 / .NET 9 的新应用可以直接引用;基于 .NET Standard 2.0 的兼容面(如 .NET Framework、.NET Core 3.1 等运行时)依然可以获得 AutoMapper 的能力;
- 低于这些版本的目标框架(例如旧的 .NET Core 版本)将无法再直接引用 15.0 的程序集,需要先升级运行时或继续停留在旧版本。
从当前仓库的工程文件 src/AutoMapper/AutoMapper.csproj 可以看到更完整的实际构建矩阵:
<TargetFrameworks>netstandard2.0;net8.0;net9.0;net10.0</TargetFrameworks>即除升级指南列出的三个框架外,仓库还额外编译了net10.0;且在 Windows 环境下还会追加net471(见该 csproj 中IsWindows条件属性)。在 Windows 上为netstandard2.0与net471目标额外引用了Microsoft.Bcl.HashCode、System.Reflection.Emit、PolySharp等兼容包(见 src/AutoMapper/AutoMapper.csproj 的ItemGroup条件引用),保证旧平台上的 API 面一致。
升级动作:升级前先确认项目的TargetFramework落在上述范围内;若项目同时面向多个框架,升级后请重新跑一遍dotnet build与单元测试,确认没有因框架切换引入的平台差异。
二、15.0 起 AutoMapper 需要许可证(License)
15.0 之后,AutoMapper 转向商业许可模式(仓库采用双许可证,见 LICENSE.md)。与 13.0 及更早版本不同,15.0 在使用时需要配置许可证密钥。
2.1 通过 DI 集成配置许可证
最常见的配置方式是在AddAutoMapper的配置回调中设置LicenseKey:
services.AddAutoMapper(cfg => { cfg.LicenseKey = "<License Key Here>"; });LicenseKey是IMapperConfigurationExpression上新增的读写属性(见 src/AutoMapper/Configuration/MapperConfigurationExpression.cs 的接口声明与MapperConfigurationExpression类中的public string LicenseKey { get; set; }实现)。
2.2 非 DI 场景:直接构造 MapperConfiguration 时配置
在不使用Microsoft.Extensions.DependencyInjection的场景下,直接在MapperConfiguration构造函数回调中设置即可:
var mapperConfiguration = new MapperConfiguration(cfg => { cfg.LicenseKey = "License Key Here"; }, loggerFactory);注意:如后文第三节所述,15.0 中
MapperConfiguration的构造函数必须传入ILoggerFactory,因此上面的loggerFactory参数不可省略。
2.3 环境变量自动发现(无需改代码)
根据 docs/source/License-configuration.md,如果代码中没有显式设置LicenseKey,AutoMapper 会从环境变量中自动查找。这在容器化、云环境和需要跨多个服务共享同一密钥的企业场景下非常方便:
AUTOMAPPER_LICENSE_KEY—— AutoMapper 专属许可证密钥;LUCKYPENNY_LICENSE_KEY—— 可在 Lucky Penny 系列产品间共享的密钥(例如 MediatR 也会读取同一变量)。因为是共享密钥,它必须对应一个包含 AutoMapper 的许可(Bundle或 AutoMapper 版本),仅含 MediatR 的许可无法通过校验。
许可证的解析优先级如下(取第一个命中的值):
- 代码中显式设置的值(
cfg.LicenseKey = "..."); AUTOMAPPER_LICENSE_KEY环境变量;LUCKYPENNY_LICENSE_KEY环境变量。
该优先级在源码 src/AutoMapper/Licensing/LicenseAccessor.cs 中有明确实现:
internal static string ResolveLicenseKey(string explicitKey) => explicitKey ?? Environment.GetEnvironmentVariable(AutoMapperLicenseKeyEnvVariable) ?? Environment.GetEnvironmentVariable(SharedLicenseKeyEnvVariable);使用环境变量时无需改代码——不设置LicenseKey,正常注册 AutoMapper 即可。
2.4 许可证的校验机制与日志行为
许可证密钥本身是一个由 Lucky Penny 软件签发的 JWT(JSON Web Token)。源码 src/AutoMapper/Licensing/LicenseAccessor.cs 使用Microsoft.IdentityModel.JsonWebTokens的JsonWebTokenHandler结合内置 RSA 公钥完成签名验证,并从中解析出account_id、customer_id、sub_id、iat、exp、edition、type、perpetual等声明(见 src/AutoMapper/Licensing/License.cs)。
校验结果只通过标准日志输出,不会阻塞或降级任何映射功能(详见 docs/source/License-configuration.md 的 "License Enforcement" 一节):
- INFO:许可证有效;
- WARNING:缺少许可证(未配置密钥),提示 "You do not have a valid license key..."(见 src/AutoMapper/Licensing/LicenseValidator.cs);
- ERROR:许可证无效或已过期。
没有中心许可服务器、没有出站 HTTP 调用、不会禁用或降级任何功能。日志记录在标准Microsoft.Extensions.Logging下,日志类别名为LuckyPennySoftware.AutoMapper.License。
值得注意的实现细节:许可证校验发生在MapperConfiguration构造路径中,但它被调度到专用后台线程异步执行(见 src/AutoMapper/Configuration/MapperConfiguration.cs 的注释与Task.Factory.StartNew(ValidateLicense, ...)),以避免在懒构建 DI 单例时因线程池饥饿导致整个应用死锁(源码注释中标注了 issue #4640)。
2.5 许可证相关的常见问题处理
重复的许可证日志:如果启动时看到多条许可证日志,通常是 AutoMapper 被注册或配置了多次。
- 多次调用
AddAutoMapper是安全的:第一次调用注册IMapper后,后续调用会被忽略(见 src/AutoMapper/ServiceCollectionExtensions.cs 中if (services.Any(sd => sd.ServiceType == typeof(IMapper))) return services;的去重逻辑),不会产生额外的许可证校验; - 但手动创建多个
MapperConfiguration实例时,每个实例都会独立执行一次许可证校验并各输出一条日志。避免方式是创建并复用单个MapperConfiguration(通常注册为单例):
var config = new MapperConfiguration(cfg => { cfg.LicenseKey = "key"; }, loggerFactory); var mapper = new Mapper(config);若确实需要多个配置(如集成测试或模块化场景),可通过过滤日志类别来静默重复消息:
builder.Logging.AddFilter("LuckyPennySoftware.AutoMapper.License", LogLevel.None);客户端分发场景(Blazor WASM / WPF / MAUI / 桌面应用):不应把许可证密钥写入客户端,否则会把机密传输给终端用户。正确做法是省略许可证密钥配置,并静默该日志类别(同上AddFilter)。缺失或无效的许可证不会以任何方式影响运行时行为。
三、破坏性变更(Breaking Changes)与迁移
15.0 有两个必须关注的破坏性变更,升级时会导致编译错误,需要逐一适配。
3.1AddAutoMapper:所有重载都必须提供配置回调
由于 15.0 必须提供许可证,AddAutoMapper的所有重载现在都要求第一个参数是Action<IMapperConfigurationExpression>。以最简单的程序集标记类型用法为例:
// 之前(14.x 及以下) services.AddAutoMapper(typeof(Program)); // 现在(15.0) services.AddAutoMapper(cfg => cfg.LicenseKey = "<License Key Here>", typeof(Program));升级指南强调:"This method parameter is first for allAddAutoMapperoverloads."——即该配置回调参数在所有重载中均位于第一个参数位置。
从源码 src/AutoMapper/ServiceCollectionExtensions.cs 可以确认,当前仓库中所有AddAutoMapper重载的签名形态为:
AddAutoMapper(this IServiceCollection services, Action<IMapperConfigurationExpression> configAction) AddAutoMapper(this IServiceCollection services, Action<IMapperConfigurationExpression> configAction, params Assembly[] assemblies) AddAutoMapper(this IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction, params Assembly[] assemblies) AddAutoMapper(this IServiceCollection services, Action<IMapperConfigurationExpression> configAction, IEnumerable<Assembly> assemblies, ServiceLifetime serviceLifetime = ServiceLifetime.Transient) AddAutoMapper(this IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction, IEnumerable<Assembly> assemblies, ServiceLifetime serviceLifetime = ServiceLifetime.Transient) AddAutoMapper(this IServiceCollection services, Action<IMapperConfigurationExpression> configAction, params Type[] profileAssemblyMarkerTypes) AddAutoMapper(this IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction, params Type[] profileAssemblyMarkerTypes) AddAutoMapper(this IServiceCollection services, Action<IMapperConfigurationExpression> configAction, IEnumerable<Type> profileAssemblyMarkerTypes, ServiceLifetime serviceLifetime = ServiceLifetime.Transient) AddAutoMapper(this IServiceCollection services, Action<IServiceProvider, IMapperConfigurationExpression> configAction, IEnumerable<Type> profileAssemblyMarkerTypes, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)可以看到不再存在任何"只有程序集/标记类型、没有配置回调"的重载。需要程序集扫描时,配置回调与程序集参数并存:
// 扫描指定程序集中的 Profile 与 [AutoMap] 映射,同时配置许可证 services.AddAutoMapper( cfg => cfg.LicenseKey = "<License Key Here>", typeof(Program).Assembly);AddAutoMapper的实际注册逻辑位于 src/AutoMapper/ServiceCollectionExtensions.cs 的AddAutoMapperClasses私有方法中:它会将配置回调以IOptions<MapperConfigurationExpression>的形式注册,扫描程序集并尝试以Transient生命周期注册各类解析器/转换器实现,最后注册IConfigurationProvider(单例)与IMapper(默认Transient,可通过serviceLifetime参数调整)。关于 DI 集成的完整说明可参考 docs/source/Dependency-injection.md。
迁移清单:
- 逐个检查项目中所有
AddAutoMapper调用点,为其补充cfg => cfg.LicenseKey = ...作为首个参数(即使你已经通过环境变量提供密钥,也需要传入一个非空回调,例如cfg => { }或仍显式赋值,以保证编译通过); - 若之前依赖
AddAutoMapper的无参形式,请补齐回调参数。
3.2MapperConfiguration:构造函数现在必须传入ILoggerFactory
15.0 中MapperConfiguration的构造函数签名变为:
public MapperConfiguration( MapperConfigurationExpression configurationExpression, ILoggerFactory loggerFactory)该loggerFactory参数用于诊断(diagnostics)。源码 src/AutoMapper/Configuration/MapperConfiguration.cs 显示,_loggerFactory字段会被保存,并在构造路径中传递给LicenseAccessor与LicenseValidator(见 src/AutoMapper/Configuration/MapperConfiguration.cs 与 src/AutoMapper/Licensing/LicenseValidator.cs),许可证校验的 INFO/WARNING/ERROR 日志正是通过这个工厂创建的LuckyPennySoftware.AutoMapper.License记录器输出的。
除此之外,当前仓库还提供了基于配置回调的构造重载:
public MapperConfiguration(Action<IMapperConfigurationExpression> configure, ILoggerFactory loggerFactory) : this(Build(configure), loggerFactory) { }(见 src/AutoMapper/Configuration/MapperConfiguration.cs)。因此非 DI 场景下最直接的迁移写法为:
using Microsoft.Extensions.Logging.Abstractions; // 无外部容器时可用 NullLoggerFactory 充当诊断记录器 var config = new MapperConfiguration(cfg => { cfg.LicenseKey = "<License Key Here>"; }, new NullLoggerFactory()); var mapper = new Mapper(config);在 DI 容器中,AddAutoMapper会自动从容器解析ILoggerFactory并完成构造(见 src/AutoMapper/ServiceCollectionExtensions.cs 的sp.GetRequiredService<ILoggerFactory>()),无需手动处理。
迁移清单:
- 所有
new MapperConfiguration(...)的调用点都需要补充ILoggerFactory参数; - 在 DI 应用中优先使用
AddAutoMapper由容器注入;在控制台、测试等无容器场景可使用NullLoggerFactory(来自Microsoft.Extensions.Logging.Abstractions)作为诊断用记录器; - 升级后观察启动日志中
LuckyPennySoftware.AutoMapper.License类别的输出,确认许可证状态符合预期。
四、完整的升级迁移示例
综合上述所有变更,一个典型的 15.0 迁移示例如下。
ASP.NET Core(DI 场景):
// Program.cs / Startup builder.Services.AddAutoMapper(cfg => { cfg.LicenseKey = "License Key Here"; // 或依赖 AUTOMAPPER_LICENSE_KEY 环境变量 // 其他全局配置,如 cfg.AddProfile<MyProfile>(); }, typeof(Program).Assembly);无 DI 容器场景:
using Microsoft.Extensions.Logging.Abstractions; var config = new MapperConfiguration(cfg => { cfg.LicenseKey = "License Key Here"; }, new NullLoggerFactory()); var mapper = new Mapper(config);补充建议:
- 升级后运行
AssertConfigurationIsValid()(详见 docs/source/Configuration-validation.md)验证所有映射配置; - 若项目此前使用了 13.0 升级时引入的
Context.State、移除了AllowAdditiveTypeMapCreation等特性,可一并回顾 docs/source/13.0-Upgrade-Guide.md 确认基线;15.0 的完整发布说明可查阅仓库 Release 标签页。
五、总结
AutoMapper 15.0 是一次引入商业模式与 API 收敛的重要升级,核心变化可归纳为三点:
- 目标框架:.NET 8、.NET 9 与 .NET Standard 2.0(仓库构建矩阵中还包含 net10.0 与 Windows 下的 net471);
- 许可认证:通过
cfg.LicenseKey或AUTOMAPPER_LICENSE_KEY/LUCKYPENNY_LICENSE_KEY环境变量提供密钥,校验结果仅以LuckyPennySoftware.AutoMapper.License类别的日志呈现,不限制任何功能; - 两个破坏性变更:
AddAutoMapper所有重载的首参数必须为配置回调;MapperConfiguration构造函数必须传入ILoggerFactory(用于许可证等诊断日志)。
按照本文的迁移清单逐项核对AddAutoMapper调用点与MapperConfiguration构造点,即可将项目平稳升级到 15.0。若需深入了解许可证的完整配置方式(含客户端分发、重复日志抑制等场景),请阅读 docs/source/License-configuration.md;DI 集成的完整能力说明见 docs/source/Dependency-injection.md。
- 后端
【免费下载链接】AutoMapper
A convention-based object-object mapper in .NET.
相关推荐
Pyrite64碰撞库源码导读:从aabbTree到capsuleSweep的12个模块拆解
Pyrite64碰撞库源码导读:从aabbTree到capsuleSweep的12个模块拆解 Pyrite64 是一款基于 libdragon 与 tiny3d
Vitess 15.0 升级指南:破坏性变更、新特性与迁移实操全解析
Vitess 15.0 升级指南:破坏性变更、新特性与迁移实操全解析 本篇技术指南以 Vitess 15.0.0 官方 Release Summary 为主体骨
数据库分布式数据库云原生后端数据存储Serverless Framework V4 升级完全指南:许可与认证变更、破坏性更新及从 V3 迁移的实战要点
Serverless Framework V4 升级完全指南:许可与认证变更、破坏性更新及从 V3 迁移的实战要点 本篇指南以仓库文档 docs/sf/guid
开发工具CLI云原生后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考