日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

【.NET Core 3.0】小技巧 || 原生DI一对多注入

發(fā)布時間:2023/12/4 asp.net 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【.NET Core 3.0】小技巧 || 原生DI一对多注入 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文是一個技巧文章,內(nèi)容很短,但是被提問的頻率很高,所以記錄下來,以待大家不時之需。

以下的代碼,是通過原生的依賴注入來講解的,其他的第三方框架,可以自己自定義擴展,效果是一樣的,那咱們先來回顧下依賴注入,都有哪幾種情況。

?一、依賴注入有哪幾種情況

關(guān)于依賴注入,其實我已經(jīng)寫了很多的文章,也錄了很多的視頻了,這里就不過多的解析什么了,無論是原理,還是好處,甚至是使用場景等等,如果你還不是很了解,可以看看我的視頻。

https://www.bilibili.com/video/av58096866?p=5

https://www.bilibili.com/video/av73194514

上邊的這個是基礎(chǔ)和核心知識點,下邊的是我直播的時候,手寫的代碼,可以根據(jù)自己的需要去查看。

總體來說,我一直講的依賴注入的方式,都是面向抽象的 很常見的:一個類對應(yīng)一個接口。那還有其他的注入情況么?當(dāng)然還有很多,比如:

1、單獨的一個類注入;

2、一個類繼承了多個接口;

3、一個接口有多個實現(xiàn)類;

當(dāng)然,除了上邊這三個,還有單獨類的 AOP 操作等等,一個類對應(yīng)一個接口的情況,我們已經(jīng)說了很多了,這里就不說了,一個類多個接口的,這個也不用說,其實就和一個類對應(yīng)一個接口,是一個效果,那我們就先說說注入單獨的一個類和,一個接口對應(yīng)多個實現(xiàn),這兩種情況吧。

單獨注入一個類很簡單,大家都知道,依賴注入,其實就是實例化的過程,然后管理我們的對象的生命周期,降低耦合等等多個好處。

那我們既然是實例化的過程,簡單啊,放到容器,直接使用它!

/// <summary> /// 1、定義一個單獨類,不繼承任何 /// </summary> public class OneSeparateService { /// <summary> /// 寫一個方法,可以通過類型返回不同內(nèi)容 /// </summary> /// <param name="type"></param> /// <returns></returns> public string SayHello(string type="") {
if (type == "English") { return "Hello"; } else { ?????????????return?"你好"; } } }
//?2、注入容器services.AddScoped<OneSeparateService>();
/// <summary>/// 3、構(gòu)造函數(shù)注入/// </summary>/// <param name="oneSeparateService"></param>public WeatherForecastController(OneSeparateService oneSeparateService){ _oneSeparateService = oneSeparateService;}

?//?4、調(diào)用 [HttpGet] public object Get() { // 依賴注入,就等于下邊的直接new一個實例 //OneSeparateService oneSeparateService = new OneSeparateService(); return oneSeparateService.SayHello("English");?}

我們只需要直接構(gòu)造函數(shù)注入,即可使用,有種靜態(tài)方法的意味,是不是很簡單!當(dāng)然很簡單啦,因為今天我們不是說這個的,說這個僅僅是一個開胃菜,體會一下注入的過程而已。

好啦,熱身完成,下面,我們就詳細的說說如何實現(xiàn)一個接口多個實現(xiàn)類吧。

?二、如何注入一對多

既然說到了一對多,那現(xiàn)在我們就來模式一下數(shù)據(jù):

/// <summary>?///?1、定義一個接口 /// </summary> public interface IMoreImplService { string SayWelocome();?}??///?<summary> /// 2、分別定義兩個實現(xiàn)類 /// </summary> public class WelcomeChineseService : IMoreImplService { public string SayWelocome() { return "歡迎"; } }
public class WelcomeEnglishService : IMoreImplService { public string SayWelocome() { return "Welcome"; }?}

然后我們準備好了,該注入了,你可能會說,簡單呀!直接注入然后調(diào)用不就好了:

services.AddScoped<IMoreImplService, WelcomeChineseService>();services.AddScoped<IMoreImplService,?WelcomeEnglishService>();

然后直接調(diào)用

public WeatherForecastController(IMoreImplService moreImplService) { _moreImplService = moreImplService; }
[HttpGet] public object Get() { return _moreImplService.SayWelocome();??}

這個時候,是不是有點兒懵,嗯?那我現(xiàn)在到底調(diào)用的是哪個實現(xiàn)類呀,我們運行看看效果就知道了:

可以看到是?Welcome?,正好和我們的注入順序是一致的,就是說,無論注入多少個,最終是最后一個生效,就好像 = new () 了好幾次,把之前的實例給覆蓋了一樣,你應(yīng)該懂了吧,不信的話,可以把注入的順序換換,就知道啦。

請記住,剛剛我用的是?好像?字眼,真的是被覆蓋掉了么,我們來看看就知道了,既然是注入了多個,我們就把多個實例都拿出來:

/// <summary>?///?1、將多個接口實例關(guān)系全部注入 /// </summary> /// <param name="moreImplServices"></param> public WeatherForecastController(IEnumerable<IMoreImplService> moreImplServices) { // 注意這里是復(fù)數(shù) _moreImplServices = moreImplServices; }
[HttpGet] public object Get() { var result = ""; // 調(diào)用多次輸出 foreach (var item in _moreImplServices) { result += item.SayWelocome() + "\n"; }
return result;?}

詳細這個時候,你應(yīng)該猜得出來答案了吧:

把兩個實例都打印了出來,這就說明一個問題,我們在容器里,并不是在注入的時候,后來的把前邊的給覆蓋掉了,而是 本來容器里就有多個接口實例映射關(guān)系 ,只是我們在 controller 控制器里取的時候,只獲取了最后一個而已!

那明白了這個問題,我們就很開心了,容器里還是都有的,我們還是可以按照我們的需要,取出想要的某一個,那我們就猜想了,如何區(qū)分呢,在文章開頭,我們定義方法的時候,就是想著用一個 type ,那這里我們能不能用一個別名來做區(qū)分呢,欸,重頭戲來了:

/// <summary>?///?定義一個服務(wù)工廠,Singleton注入 /// 注意這個不是真正意義上的工廠,只是提供服務(wù)的存取 /// </summary> public class SingletonFactory { // 定義一個字典,存儲我們的接口服務(wù)和別名 Dictionary<Type, Dictionary<string, object>> serviceDict; public SingletonFactory() { serviceDict = new Dictionary<Type, Dictionary<string, object>>(); }
/// <summary> /// 根據(jù)別名,獲取接口實例 /// </summary> /// <typeparam name="TService"></typeparam> /// <param name="id"></param> /// <returns></returns> public TService GetService<TService>(string id) where TService : class { // 獲取接口的類型 var serviceType = typeof(TService); // out 方法,先獲取某一個接口下的,<別名,實例>字典 if (serviceDict.TryGetValue(serviceType, out Dictionary<string, object> implDict)) { // 根據(jù)別名,獲取接口的實例對象 if (implDict.TryGetValue(id, out object service)) { // 強類型轉(zhuǎn)換 return service as TService; } } return null; }
/// <summary> /// 將實例和別名 匹配存儲 /// </summary> /// <typeparam name="TService"></typeparam> /// <param name="service"></param> /// <param name="id"></param> public void AddService<TService>(TService service, string id) where TService : class { var serviceType = typeof(TService); // 對象實例判空 if (service != null) { // 如果不存在,則填充 if (serviceDict.TryGetValue(serviceType, out Dictionary<string, object> implDict)) { implDict[id] = service; } else { implDict = new Dictionary<string, object>(); implDict[id] = service; serviceDict[serviceType] = implDict; } } }?}

上邊的代碼相信應(yīng)該能大致看的明白,看不明白也沒關(guān)系,主要一句話概括,就是使用了一個服務(wù),維護一個字典,利用泛型,先把對象實例和別名,分配存儲到字典,然后再根據(jù)別名或者指定接口的對象實例

就是把接口下的實現(xiàn)類,都 new 出來實例,然后匹配上別名,說白了,就是我們的 type,然后再輸出出來。

那下一步,我們就需要先把這個單例服務(wù)給注入進去:

SingletonFactory singletonFactory = new SingletonFactory(); singletonFactory.AddService<IMoreImplService>(new WelcomeChineseService(), "Chinese"); singletonFactory.AddService<IMoreImplService>(new WelcomeEnglishService(), "English");
?services.AddSingleton(singletonFactory);

這個應(yīng)該都能看的懂,唯一的小問題,可能會問,為啥要把我們的 singletonFactory 給 Singleton 注入?

因為我這是一個對象實例,只能是單例了,而且里邊的多個服務(wù)因為已經(jīng)new實例過了,所以沒辦法控制生命周期

最后我們就來調(diào)用看看:

// 各自定義需要的多個字段private readonly IMoreImplService moreImplServiceChinese;private readonly IMoreImplService moreImplServiceEnglish;

public WeatherForecastController(SingletonFactory singletonFactory){ this.singletonFactory = singletonFactory;
// 根據(jù)別名獲取服務(wù) moreImplServiceChinese = singletonFactory.GetService<IMoreImplService>("Chinese"); moreImplServiceEnglish = singletonFactory.GetService<IMoreImplService>("English");}
[HttpGet("/welcome")]public object GetWelcome(){ return moreImplServiceChinese.SayWelocome();}

結(jié)果我們不用看了,已經(jīng)成功了,最后我們再來回顧一下這種寫法的步驟:

1、定義一個單例服務(wù)類,將我們的多個對象new出來實例,和別名對應(yīng)存儲起來;

2、將單例類實例化后,注入服務(wù)容器;

3、控制器獲取單例類,并根據(jù)別名獲取相對應(yīng)的服務(wù);

到了這里,我們就已經(jīng)完成了,是不是到了這里,感覺是已經(jīng)完成了,但是又感覺哪里不是很舒服,比如這樣注入的實例都是單例的,那這樣不是很合適呀?

?三、簡單工廠模式注入【推薦】

如何才能適應(yīng)不同的生命周期呢,我這里提供第二個方法:

// 先把多個實現(xiàn)類服務(wù)注入進去 services.AddScoped<WelcomeChineseService>(); services.AddScoped<WelcomeEnglishService>();
// 然后通過簡單工廠模式,針對不同的 key 來獲取指定的對象實例 services.AddScoped(factory => { Func<string, IMoreImplService> accesor = key => { if (key.Equals("Chinese")) {?????????????// 因為這里是從容器獲取服務(wù)實例的,所以我們可以控制生命周期 return factory.GetService<WelcomeChineseService>(); } else if (key.Equals("English")) { return factory.GetService<WelcomeEnglishService>(); } else { throw new ArgumentException($"Not Support key : {key}"); } }; return accesor;?});

大家可以看一下,我們用的是 Scope 方式注入的,三種生命周期都可以,接下看就是調(diào)用了:

// 將我們的規(guī)則 Func 構(gòu)造函數(shù)注入 private readonly Func<string, IMoreImplService> _serviceAccessor;
public WeatherForecastController(Func<string, IMoreImplService> serviceAccessor) { // 獲取特定接口的服務(wù)訪問器,然后根據(jù)別名獲取 _serviceAccessor = serviceAccessor;?????//?這里的別名,你可以配置到 appsetting.json?文件里,動態(tài)的修改獲取對象實例???? // 然后再在接口中配置一個字段 string ImplementKeyName { get; } moreImplServiceChinese = _serviceAccessor("Chinese"); moreImplServiceEnglish = _serviceAccessor("English"); }

[HttpGet("/welcome")] public object GetWelcome() { return moreImplServiceChinese.SayWelocome() + "\n" + moreImplServiceEnglish.SayWelocome();?}

為了演示效果,我把Service服務(wù)的構(gòu)造函數(shù),增加一個動態(tài)時間,來判斷是否滿足Scope需求,那現(xiàn)在我們就來看看效果吧:

public class WelcomeChineseService : IMoreImplService { public DateTime Now { get; set; } public WelcomeChineseService() { Now = DateTime.Now; } public string SayWelocome() { return "歡迎" + Now; }?}


好啦,最后我們來總結(jié)一下這個方法的優(yōu)點:

1、可以實現(xiàn)一個接口對應(yīng)多個實現(xiàn)類的注入和獲取;

2、實例別名可以配置到 appsettings.json 里,動態(tài)獲取指定服務(wù);

3、可以指定生命周期;

4、更直觀,更簡單;

雖然這種簡單工廠的寫法,還是不夠優(yōu)雅,但是畢竟這種一個接口多個實現(xiàn)類的方法本身就不是很優(yōu)雅,好啦,今天就分享到這里吧。

總結(jié)

以上是生活随笔為你收集整理的【.NET Core 3.0】小技巧 || 原生DI一对多注入的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。