最近有個需求就是一個抽象倉儲層介面方法需要SqlServer以及Oracle兩種實現方式,為了靈活我在依賴註入的時候把這兩種實現都給註入進了依賴註入容器中,但是在服務呼叫的時候總是獲取到最後註入的那個方法的實現,這時候就在想能不能實現動態的選擇使用哪種實現呢?如果可以的話那麼我只需要在配置檔案中進行相應的配置即可獲取到正確的實現方法的呼叫,這樣的話豈不快哉!今天我們就來一起探討下實現這種需求的幾種實現方式吧。
作者:依樂祝
原文地址:https://www.cnblogs.com/yilezhu/p/10236163.html
程式碼演示
在開始實現的方式之前,我們先模擬下程式碼。由於真實系統的結構比較複雜,所以這裡我就單獨建一個類似的專案結構程式碼。專案如下圖所示:
接下來我來詳細說下上面的結果作用及程式碼。
-
MultiImpDemo.I 這個專案是介面專案,裡面有一個簡單的介面定義
ISayHello
,程式碼如下:public interface ISayHello { string Talk(); }
很簡單,就一個模擬講話的方法。
-
MultiImpDemo.A 這個類庫專案是介面的一種實現方式,裡面有一個
SayHello
類用來實現ISayHello
介面,程式碼如下: -
MultiImpDemo.B 這個類庫專案是介面的另一種實現方式,裡面也有一個
SayHello
類用來實現ISayHello
介面,程式碼如下: -
MultiImpDemo.Show 這個就是用來顯示我們模擬效果的API專案,首選我們在
ConfigureServices
中加入如下的程式碼來進行上述兩種實現方式的註入: -
在api實現裡面獲取服務併進行模擬呼叫:
程式碼很簡單對不對?你應該看的懂吧,這時候我們執行起來專案,然後訪問API’api/values’這個介面,結果總是顯示如下的結果:
兩種需求對應兩種實現
這裡有兩種業務需求!第一種業務中只需要對其中一種實現方式進行呼叫,如:業務需要SqlServer資料庫的實現就行了。第二種是業務中對這兩種實現方式都有用到,如:業務急需要用到Oracle的資料庫實現同時也有用到SqlServer的資料庫實現,需要同時往這兩個資料庫中插入相同的資料。下麵分別對這兩種需求進行解決。
業務中對這兩種實現方式都有用到
針對這種情況有如下兩種實現方式:
-
第二種實現方式
其實,在ASP.NET Core中,當你對一個介面註冊了多個實現的時候,建構式是可以註入一個該介面集合的,這個集合裡是所有註冊過的實現。
下麵我們先改造下
ConfigureServices
,分別註入下這兩種實現接著繼續改造下註入的方式,這裡我們直接註入
IEnumerable
如下程式碼所示:然後執行起來看下效果吧
-
利用
AddTransient
的擴充套件方法public static IServiceCollection AddTransient(this IServiceCollection services, Func implementationFactory) where TService : class;
然後根據我們的配置的實現來進行服務實現的獲取。下麵就讓我們利用程式碼來實現一番吧:然後我們具體呼叫的依賴註入的方式需要變化一下:
然後執行看下效果吧:
可以看到A跟B的實現都獲取到了!效果實現!
業務只需要對其中一種實現方式的呼叫
這時候我們可以根據我們預設的配置來動態獲取我們所需要的實現。這段話說的我自己都感覺拗口。話不多少,開魯吧!這裡我將介紹三種實現方式。
-
根據我們的配置檔案中設定的key來進行動態的註入。
這種方式實現之前首先得進行相應的配置,如下所示:
"CommonSettings": { "ImplementAssembly": "MultiImpDemo.A" }
然後在註入的時候根據配置進行動態的進行註入:
services.AddTransient<ISayHello, A.SayHello>(); services.AddTransient<ISayHello, B.SayHello>();
然後在服務呼叫的時候稍作修改:
OK,到這裡執行一下看下效果吧!然後改下配置檔案再看下效果!
-
第二種實現方式,即介面引數的方式這樣可以避免上個方法中反射所帶來的效能損耗。
這裡我們改造下介面,介面中加入一個程式集的屬性,如下所示:
public interface ISayHello { string ImplementAssemblyName { get; } string Talk(); }
對應的A跟B中的實現程式碼也要少做調整:
A:
public string ImplementAssemblyName => "MultiImpDemo.A"; public string Talk() { return "Talk from A.SayHello"; }
B:
public string ImplementAssemblyName => "MultiImpDemo.B"; public string Talk() { return "Talk from B.SayHello"; }
然後,在實現方法呼叫的時候稍微修改下:
效果自己執行下看下吧!
-
第三種實現是根據配置進行動態的註冊
首先修改下
ConfigureServices
方法: -
這樣的話就會根據我們的配置檔案來進行動態的註冊,然後我們像往常一樣進行服務的調取即可:
private readonly ISayHello _sayHello; public ValuesController(ISayHello sayHello) { _sayHello = sayHello; } // GET api/values [HttpGet] public ActionResultstring>> Get() { return new string[] { _sayHello.Talk() }; }
執行即可得到我們想要的效果!
總結
本文從具體的業務需求入手,根據需求來或動態的進行對應服務的獲取,或同時使用兩個不同的實現!希望對您有所幫助!如果您有更多的實現方法可以在下方留言,或者加入.NET Core實戰千人群跟637326624大夥進行交流,最後感謝您的閱讀!