ASP.NET Core中AutoMapper自定义值解析器的DI与参数传递问题
解决方案:在AutoMapper解析器中同时使用DI服务和自定义参数
你遇到的这个场景确实很常见——既要依赖注入获取服务(比如IHttpContextAccessor),又要传递自定义配置参数,同时不想污染IMapper.Map的调用逻辑。下面是几个符合你需求的可行方案:
方案1:利用AutoMapper配置的Items存储自定义参数(推荐)
这个方案最简洁,不需要额外的工厂类,完全依托AutoMapper自身的机制:
步骤1:在Profile中配置映射时存入自定义参数
在MyProfile.cs里,我们把自定义参数放到映射配置的Items集合中,同时使用泛型版本的ResolveUsing让DI容器创建解析器:
public class MyProfile : Profile { public MyProfile() { CreateMap<Entity1, Dto1>() .ForMember(x => x.CustomProperty, opt => { // 存入自定义参数,键名可以自定义 opt.Items["ResolverSomeValue"] = "Important value"; // 使用泛型ResolveUsing,让DI创建解析器实例 opt.ResolveUsing<MyCustomPropertyResolver>(); }); } }
步骤2:修改解析器,注入DI服务并读取配置参数
解析器通过构造函数注入IHttpContextAccessor,然后在Resolve方法中从ResolutionContext.Options.Items读取我们预先存入的自定义参数:
public class MyCustomPropertyResolver : IValueResolver<Entity1, Dto1, string> { private readonly IHttpContextAccessor _httpContextAccessor; // 通过DI注入所需服务 public MyCustomPropertyResolver(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public string Resolve(Entity1 source, Dto1 destination, string destMember, ResolutionContext context) { // 从配置Items中读取自定义参数 if (context.Options.Items.TryGetValue("ResolverSomeValue", out var valueObj) && valueObj is string someValue) { // 结合DI服务和自定义参数实现业务逻辑 var currentUser = _httpContextAccessor.HttpContext?.User.Identity?.Name; return $"Custom Value: {someValue}, Current User: {currentUser ?? "Anonymous"}"; } return string.Empty; } }
步骤3:确保DI服务注册正确
在Program.cs(或Startup.cs)中注册AutoMapper和IHttpContextAccessor:
var builder = WebApplication.CreateBuilder(args); // 注册AutoMapper,指定Profile所在程序集 builder.Services.AddAutoMapper(typeof(MyProfile)); // 注册IHttpContextAccessor builder.Services.AddHttpContextAccessor(); // ...其他服务注册
这样做的好处:
- 解析器完全由DI容器管理,能正常注入所有所需服务
- 自定义参数在映射配置阶段就确定,调用
mapper.Map<Entity1, Dto1>(entity)时不需要传入任何额外参数 - 避免了静态属性带来的线程安全和依赖污染问题
方案2:使用工厂类封装解析器的创建(适合动态参数场景)
如果你的自定义参数不是在配置映射时固定的,而是需要动态传入,可以用一个工厂类来封装解析器的创建逻辑:
步骤1:创建解析器工厂
public class MyCustomResolverFactory { private readonly IHttpContextAccessor _httpContextAccessor; // 工厂注入DI服务 public MyCustomResolverFactory(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } // 工厂方法接受自定义参数,返回解析器实例 public MyCustomPropertyResolver Create(string someValue) { return new MyCustomPropertyResolver(_httpContextAccessor, someValue); } }
步骤2:修改解析器接受DI服务和自定义参数
public class MyCustomPropertyResolver : IValueResolver<Entity1, Dto1, string> { private readonly IHttpContextAccessor _httpContextAccessor; private readonly string _someValue; public MyCustomPropertyResolver(IHttpContextAccessor httpContextAccessor, string someValue) { _httpContextAccessor = httpContextAccessor; _someValue = someValue; } public string Resolve(Entity1 source, Dto1 destination, string destMember, ResolutionContext context) { var currentUser = _httpContextAccessor.HttpContext?.User.Identity?.Name; return $"Custom Value: {_someValue}, Current User: {currentUser ?? "Anonymous"}"; } }
步骤3:在Profile中使用工厂创建解析器
public class MyProfile : Profile { public MyProfile() { CreateMap<Entity1, Dto1>() .ForMember(x => x.CustomProperty, opt => opt.ResolveUsing(context => { // 从上下文获取工厂实例 var factory = context.GetService<MyCustomResolverFactory>(); // 创建解析器并传入动态参数 var resolver = factory.Create("Important value"); return resolver.Resolve(context.SourceValue as Entity1, context.DestinationValue as Dto1, destMember, context); })); } }
步骤4:注册工厂到DI容器
builder.Services.AddScoped<MyCustomResolverFactory>();
这个方案适合参数需要动态生成的场景,但比方案1稍显繁琐,建议优先使用方案1。
内容的提问来源于stack exchange,提问作者Ehab Kashkash




