You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8中如何在单例服务内访问作用域服务?

单例服务中访问作用域服务的.NET 8解决方案

问题背景

需要在单例服务内访问作用域服务IUserRequestData(存储当前请求上下文信息,类似HttpContext.Items),现有实现尝试在CustomMapper(实现IMapperConfiguration)的构造函数中注入IUserRequestData,但.NET DI禁止单例依赖作用域服务。若将IMapperConfiguration设为作用域服务,每次请求调用Map()会重复配置全局单例的TypeAdapterConfig,造成冗余或逻辑覆盖。

现有代码:

public class CustomMapper(IUserRequestData data) : IMapperConfiguration 
{
    public void Map()
    {
        TypeAdapterConfig<User, UserResponse>.NewConfig()
            .Map(
                u => u.GroupName,
                src => src.Groups.FirstOrDefault(g => 
                          g.CnId == data.GetCurrentCnId()
                       ) != null
                    ? src.Groups.FirstOrDefault(
                        g => g.CnId == data.GetCurrentCnId()
                      )!.Name
                    : ""
            );
        TypeAdapterConfig<UserUpdateInput, User>.NewConfig();
    }
}

可行解决方案

方案1:全局配置映射,执行时动态获取作用域服务

TypeAdapterConfig是全局单例,应在应用启动时完成配置,将依赖请求上下文的逻辑移到映射执行阶段:

  1. 将CustomMapper注册为单例,注入IServiceProvider:
public class CustomMapper(IServiceProvider serviceProvider) : IMapperConfiguration 
{
    public void Configure()
    {
        TypeAdapterConfig<User, UserResponse>.NewConfig()
            .Map(dest => dest.GroupName, src => 
            {
                var requestData = serviceProvider.GetRequiredService<IUserRequestData>();
                var currentCnId = requestData.GetCurrentCnId();
                var group = src.Groups.FirstOrDefault(g => g.CnId == currentCnId);
                return group?.Name ?? "";
            });
        
        TypeAdapterConfig<UserUpdateInput, User>.NewConfig();
    }
}
  1. 应用启动时仅调用一次Configure()完成全局配置:
// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IUserRequestData, UserRequestData>();
builder.Services.AddSingleton<IMapperConfiguration, CustomMapper>();

var app = builder.Build();

// 初始化全局映射配置
var mapperConfig = app.Services.GetRequiredService<IMapperConfiguration>();
mapperConfig.Configure();

// 后续中间件配置...

方案2:使用Mapster依赖注入扩展(推荐)

利用Mapster官方DI扩展,自动在映射时从当前作用域获取服务:

  1. 安装依赖包:
Install-Package Mapster.DependencyInjection
  1. 全局配置时通过映射上下文获取服务:
public class CustomMapper : IMapperConfiguration 
{
    public void Configure()
    {
        TypeAdapterConfig<User, UserResponse>.NewConfig()
            .Map(dest => dest.GroupName, (src, context) => 
            {
                var requestData = context.GetService<IUserRequestData>();
                var currentCnId = requestData?.GetCurrentCnId() ?? default;
                var group = src.Groups.FirstOrDefault(g => g.CnId == currentCnId);
                return group?.Name ?? "";
            });
        
        TypeAdapterConfig<UserUpdateInput, User>.NewConfig();
    }
}
  1. 注册Mapster并初始化配置:
// Program.cs
builder.Services.AddMapster();
builder.Services.AddSingleton<IMapperConfiguration, CustomMapper>();

var app = builder.Build();

var mapperConfig = app.Services.GetRequiredService<IMapperConfiguration>();
mapperConfig.Configure();

方案3:作用域服务配置(不推荐)

若必须在请求周期内调整映射规则,可将IMapperConfiguration注册为作用域服务,但需避免重复覆盖全局配置:

// Program.cs
builder.Services.AddScoped<IMapperConfiguration, CustomMapper>();

在请求处理流程中调用Map()时,建议先检查配置是否已存在,避免重复创建:

public void Map()
{
    if (!TypeAdapterConfig<User, UserResponse>.Config.HasMapping)
    {
        TypeAdapterConfig<User, UserResponse>.NewConfig()
            .Map(/* ... */);
    }
    // ...
}

核心注意事项

  • .NET DI禁止单例依赖作用域服务,是为了防止作用域服务被单例长期持有导致生命周期泄漏,必须确保作用域服务仅在请求周期内被临时获取和使用。
  • TypeAdapterConfig是全局单例配置,应在应用启动时一次性完成,请求周期内重复配置会引发逻辑冲突或性能问题。

内容的提问来源于stack exchange,提问作者eestein

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 19:14:51