巧用 Lazy 解决.NET Core中的循环依赖关系
原文作者: Thomas Levesque 原文鏈接:https://thomaslevesque.com/2020/03/18/lazily-resolving-services-to-fix-circular-dependencies-in-net-core/
循環依賴的問題
在構建應用程序時,良好的設計應該應避免服務之間的循環依賴, 循環依賴是指某些組件直接或間接相互依賴,比如下面這樣
如果您不小心在.NET Core應用程序使用了依賴項注入,并且引入了以下循環依賴關系,你要知道的是,項目啟動會報一個循環依賴的錯誤,因為依賴關系周期中涉及的組件的解析將失敗,比如,你具有以下組件:
?A服務,它實現了接口IA并取決于IB?B服務,它實現了接口IB并取決于IC?C服務,它實現了接口IC并取決于IA
System.InvalidOperationException: A circular dependency was detected for the service of type 'Demo.IA'.
所以應該去避免這些設計。
注入 IServiceProvider
但是,當實際應用程序達到一定程度的復雜性時,有時可能很難避免,有一天不小心給服務添加了一個依賴項,啟動報錯了,事情突然浮出水面, 因此,您面臨一個選擇:重構,來解決循環依賴的問題,理想情況下,應該去選擇重構,但是實際情況中,可能項目比較緊,可能沒有時間重構代碼,因為要做完整的回歸測試。
一種方法是將注入 IServiceProvider 到您的類中,并services.GetRequiredService()在需要使用時使用T,例如,C我前面提到的類,最初可能看起來像這樣:
class C : IC {private readonly IA _a;public C(IA a){_a = a;}public void Bar(){..._a.Foo()...} }為了避免依賴性循環,可以注入 IServiceProvider, 然后這樣重寫它:
class C : IC {private readonly IServiceProvider _services;public C(IServiceProvider services){_services = services;}public void Bar(){...var a = _services.GetRequiredService<IA>();a.Foo();...} }由于在構建IA時不再需要解決問題C,因此中斷了循環(至少在構建過程中),并解決了問題,但是,我不太喜歡這種方法,因為這樣強制依賴了IOC,如果我使用了 Autofac 等,另一個問題是我很難看到類的依賴關系,它不明顯。
巧用?Lazy<T>
下邊的方法我利用了Lazy類,需要添加一個 IServiceCollection 的擴展,新建一個靜態類
public static IServiceCollection AddLazyResolution(this IServiceCollection services) {return services.AddTransient(typeof(Lazy<>),typeof(LazilyResolved<>)); }private class LazilyResolved<T> : Lazy<T> {public LazilyResolved(IServiceProvider serviceProvider): base(serviceProvider.GetRequiredService<T>){} }然后再 Startup.cs 中的 ConfigureServices 方法中這樣寫
services.AddLazyResolution();在依賴的類中IA,注入Lazy,當您需要使用時IA,只需訪問lazy的值 Value 即可:
class C : IC {private readonly Lazy<IA> _a;public C(Lazy<IA> a){_a = a;}public void Bar(){..._a.Value.Foo();...} }注意:不要訪問構造函數中的值,保存Lazy即可 ,在構造函數中訪問該值,這將導致我們試圖解決的相同問題。
這個解決方案不是完美的,但是它解決了最初的問題卻沒有太多麻煩,并且依賴項仍然在構造函數中明確聲明,我可以看到類之間的依賴關系。
最后
歡迎掃碼關注我們的公眾號 【全球技術精選】,專注國外優秀博客的翻譯和開源項目分享,也可以添加QQ群 897216102
總結
以上是生活随笔為你收集整理的巧用 Lazy 解决.NET Core中的循环依赖关系的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微前端架构在容器平台的应用
- 下一篇: 如何在 C# 中使用 MSMQ