1. 程式人生 > >巧用 Lazy 解決.NET Core中的迴圈依賴關係

巧用 Lazy 解決.NET Core中的迴圈依賴關係

> 原文作者: Thomas Levesque > 原文連結:[https://thomaslevesque.com/2020/03/18/lazily-resolving-services-to-fix-circular-dependencies-in-net-core/](https://thomaslevesque.com/2020/03/18/lazily-resolving-services-to-fix-circular-dependencies-in-net-core/ "https://thomaslevesque.com/2020/03/18/lazily-resolving-services-to-fix-circular-dependencies-in-net-core/") ![](https://blog-1259586045.cos.ap-shanghai.myqcloud.com/clipboard_20210111_081904.png) ### 迴圈依賴的問題 在構建應用程式時,良好的設計應該應避免服務之間的迴圈依賴, 迴圈依賴是指某些元件直接或間接相互依賴,比如下面這樣 ![](https://blog-1259586045.cos.ap-shanghai.myqcloud.com/clipboard_20210111_074635.png) 如果您不小心在.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我前面提到的類,最初可能看起來像這樣: ```csharp class C : IC { private readonly IA _a; public C(IA a) { _a = a; } public void Bar() { ... _a.Foo() ... } } ``` 為了避免依賴性迴圈,可以注入 IServiceProvider, 然後這樣重寫它: ```csharp class C : IC { private readonly IServiceProvider _services; public C(IServiceProvider services) { _services = services; } public void Bar() { ... var a = _services.GetRequiredService(); a.Foo(); ... } } ``` 由於在構建IA時不再需要解決問題C,因此中斷了迴圈(至少在構建過程中),並解決了問題,但是,我不太喜歡這種方法,因為這樣強制依賴了IOC,如果我使用了 Autofac 等,另一個問題是我很難看到類的依賴關係,它不明顯。 ### 巧用 `Lazy` 下邊的方法我利用了Lazy類,需要新增一個 IServiceCollection 的擴充套件,新建一個靜態類 ```csharp public static IServiceCollection AddLazyResolution(this IServiceCollection services) { return services.AddTransient( typeof(Lazy<>), typeof(LazilyResolved<>)); } private class LazilyResolved : Lazy { public LazilyResolved(IServiceProvider serviceProvider) : base(serviceProvider.GetRequiredService) { } } ``` 然後再 Startup.cs 中的 ConfigureServices 方法中這樣寫 ```csharp services.AddLazyResolution(); ``` 在依賴的類中IA,注入Lazy,當您需要使用時IA,只需訪問lazy的值 Value 即可: ```csharp class C : IC { private readonly Lazy _a; public C(Lazy a) { _a = a; } public void Bar() { ... _a.Value.Foo(); ... } } ``` 注意:不要訪問建構函式中的值,儲存Lazy即可 ,在建構函式中訪問該值,這將導致我們試圖解決的相同問題。 這個解決方案不是完美的,但是它解決了最初的問題卻沒有太多麻煩,並且依賴項仍然在建構函式中明確宣告,我可以看到類之間的依賴關係。 ### 最後 歡迎掃碼關注我們的公眾號 【全球技術精選】,專注國外優秀部落格的翻譯和開源專案分享,也可以新增QQ群 897216102