之前一直做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里面使用的接口應該都是只讀的實例,這樣就不會產生更新操作,導致線程不安全的問題