You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动