如何在Microsoft默认DI作用域内替换服务实现?
如何在Microsoft默认DI的作用域内替换服务实现
针对你的场景,直接在已创建的ServiceScope上调用AddScoped是行不通的——默认DI的ServiceScope并没有提供这类方法来动态修改已注册的服务。下面给你几种可行的解决方案:
方案一:用ActivatorUtilities直接注入指定实例
这是最简洁的方式,适合仅需替换单个依赖的场景。ActivatorUtilities.CreateInstance会利用现有作用域的服务提供者解析目标服务的其他依赖,同时传入你指定的IUserSessionProvider实例:
public IActionResult ControllerMethod( [FromService] IUserSessionProvider sessionProvider, [FromServices] IServiceProvider sp) { var staticSessionProvider = new StaticDataSessionProvider( userName: sessionProvider.userName, userData: sessionProvider.userData ); ExecuteInBackground(()=>{ using(var scope = sp.CreateScope()){ // 直接创建MyJobService实例,传入自定义的sessionProvider var myJob = ActivatorUtilities.CreateInstance<MyJobService>( scope.ServiceProvider, staticSessionProvider ); myJob.Run(); } }); }
方案二:构建新的服务集合替换注册
如果你的后台作业需要多次从作用域中获取IUserSessionProvider(不止MyJobService依赖它),可以基于原作用域的服务集合创建新集合,替换IUserSessionProvider的注册后再构建新的服务提供者:
public IActionResult ControllerMethod( [FromService] IUserSessionProvider sessionProvider, [FromServices] IServiceProvider sp) { var staticSessionProvider = new StaticDataSessionProvider( userName: sessionProvider.userName, userData: sessionProvider.userData ); ExecuteInBackground(()=>{ using(var scope = sp.CreateScope()){ // 复制原服务集合 var services = new ServiceCollection(); var rootServices = sp.GetRequiredService<IServiceCollection>(); foreach (var descriptor in rootServices) { services.Add(descriptor); } // 替换IUserSessionProvider的注册为自定义实例 services.AddScoped<IUserSessionProvider>(_ => staticSessionProvider); // 构建新的作用域服务提供者 using var scopedSp = services.BuildServiceProvider(); var myJob = scopedSp.GetService<MyJobService>(); myJob.Run(); } }); }
注意:这种方式需要确保根服务提供者能获取到IServiceCollection,在ASP.NET Core中,你需要在Program.cs/Startup.cs中提前注册:
builder.Services.AddSingleton(builder.Services);
方案三:使用工厂模式封装
如果你的场景更复杂,可以注册一个IUserSessionProvider的工厂,在后台作业中指定使用静态实例:
- 先注册工厂:
// Program.cs builder.Services.AddScoped<IUserSessionProvider, HttpContextSessionProvider>(); // 注册工厂,允许传入自定义实例 builder.Services.AddScoped<Func<IUserSessionProvider, IUserSessionProvider>>(sp => customInstance => customInstance ?? sp.GetRequiredService<IUserSessionProvider>() );
- 修改
MyJobService的构造函数接受工厂:
public class MyJobService { private IUserSessionProvider _sessionProvider; public MyJobService(Func<IUserSessionProvider, IUserSessionProvider> sessionFactory) { // 默认使用DI注册的实例 _sessionProvider = sessionFactory(null); } // 提供方法设置自定义实例 public void SetSessionProvider(IUserSessionProvider provider) { _sessionProvider = provider; } public void Run() { // 使用_sessionProvider处理逻辑 } }
- 在控制器中使用:
public IActionResult ControllerMethod( [FromService] IUserSessionProvider sessionProvider, [FromServices] IServiceProvider sp) { var staticSessionProvider = new StaticDataSessionProvider( userName: sessionProvider.userName, userData: sessionProvider.userData ); ExecuteInBackground(()=>{ using(var scope = sp.CreateScope()){ var myJob = scope.ServiceProvider.GetService<MyJobService>(); myJob.SetSessionProvider(staticSessionProvider); myJob.Run(); } }); }
总结
- 如果只是单次创建
MyJobService,优先用方案一,代码简洁且无需修改原有DI配置; - 如果需要在作用域内多次获取替换后的
IUserSessionProvider,用方案二; - 复杂场景下可以考虑方案三的工厂模式,扩展性更强。
内容的提问来源于stack exchange,提问作者Thanasis Ioannidis
相关产品推荐
相关产品推荐

