如何在netcore下,愉快的使用IServiceProvider
之前一直做dotnet framework开发,依赖注入使用Autofac,Autofac的一般用法是服务启动时,将用到的接口、实现类名注入进去,
然后在服务其他地方如果使用该类时,直接在Container里面Resolve出来即可。
后来使用netcore 2.1,框架本身使用了Microsoft.Extensions.DependencyInjection,是服务启动时,把需要注入的服务通过
public void ConfigureServices(IServiceCollection services)
实现依赖注入,在服务其他地方通过构造函数把服务实例化出来,然后实现具体方法的调用。
期初,因为场景比较简单,用起来比较舒服,后来随着业务复杂,就面临了一些挑战:
1)在服务的某个方法中,不想通过构造函数,现在想使用已经注入的接口方法,咋办?
为了解决这个问题,我们引入了IHttpContextAccessor(web服务),在IHttpContextAccessor里面有HttpContext,
在HttpContext里面有个RequestServices就是IServiceProvider类型,里面有我们想要的已经注入的服务:
public abstract class ControllerBase : Controller { protected ControllerBase(IMediator mediator, ILogger logger, IHttpContextAccessor accessor) { var configuration = accessor.HttpContext.RequestServices.GetRequiredService<IConfigurationManager>(); } }
PS:使用HttpContext里面的RequestServices是有局限的,比如单元测试怎么写?(其实还是仿web的初始化,本次不讨论)
上面的使用HttpContext里面的RequestServices解决了一些问题,但是后来又遇到问题了
2)异步编程,Handler里面处理的逻辑比较耗时,所以我们打算把里面的逻辑抽出来使用异步方式,然后立刻返回结果,
立刻返回结果导致HttpContext.RequestServices变成了null,所以在异步调用逻辑中用到RequestServices的地方全部报错。
根据这个问题,我们在common项目里面增加ServiceLocator类:
public class ServiceLocator { public static IServiceProvider Services { get; private set; } public static void SetServices(IServiceProvider services) { Services = services; } }
然后在 public void ConfigureServices(IServiceCollection services)
方法里面使用
// 将注入放在全局变量里维护 ServiceLocator.SetServices(serviceProvider);
PS:尽量在该方法的最下面使用这个赋值。
然后在你服务中的其他位置使用它拿到已经注入的接口
注意:在ServiceLocator里面使用的接口应该都是只读的实例,这样就不会产生更新操作,导致线程不安全的问题