ASP.NET Core 奇淫技巧之偽屬性注入的實(shí)現(xiàn)
一.前言
開局先嘮嗑一下,許久未曾更新博客,一直在調(diào)整自己的狀態(tài),去年是我的本命年,或許是應(yīng)驗(yàn)了本命年的多災(zāi)多難,過得十分不順,不論是生活上還是工作上。還好當(dāng)我度過了所謂的本命年后,許多事情都在慢慢變好,我將會(huì)開始恢復(fù)更新博客,爭取恢復(fù)到以前的速度上(因?yàn)楣ぷ鞅容^忙,所以這個(gè)過程可能需要一段時(shí)間)。
二.關(guān)于屬性注入
說到屬性注入,我們就不得不提一下 DI(Dependency Injection),即依賴注入,用過 ASP.NET Core 的同學(xué)相信對(duì)這個(gè)詞不會(huì)陌生。ASP.NET Core 自帶了一個(gè)IOC容器,且程序運(yùn)行也是基于這個(gè)容器建立起來的,在 Startup 里的 ConfigureServices
方法就是向容器注冊(cè)類型。最直白的講,我們?cè)?ASP.NET Core 中,想使用某個(gè)類型的時(shí)候可以不用自己去 new,可以由容器通過構(gòu)造方法來注入具體的實(shí)現(xiàn)類型,而我們一般在構(gòu)造方法上定義的依賴類型都是接口,而不是去依賴具體的實(shí)現(xiàn),這里就體現(xiàn)了 SOLID 原則中的依賴倒置原則(DIP)。這也是IOC(Inversion of Control),即控制反轉(zhuǎn),不直接依賴具體實(shí)現(xiàn),將依賴交給容器去控制。上述幾者是具有一定的關(guān)聯(lián)關(guān)系的,DIP 是一種軟件設(shè)計(jì)原則,IOC 是 DIP 的具體實(shí)現(xiàn)方式,DI 是 IOC 的一種實(shí)現(xiàn)方式。
在依賴注入時(shí),我們最常用的便是通過構(gòu)造方法注入,還有另一種方式那便是屬性注入。
關(guān)于屬性注入,如果在網(wǎng)上搜索,大部分內(nèi)容都是不推薦使用,或者說慎重使用的,因?yàn)閷傩宰⑷霑?huì)造成類型的依賴關(guān)系隱藏,測試不友好等,我也同意這種說法,屬性注入可以使用,但是要謹(jǐn)慎,不能盲目使用。我的原則:在封裝框架(搭架子)時(shí)可以使用,但不能大范圍使用,只有必須使用屬性注入來達(dá)到效果的地方才會(huì)使用,用來提高使用框架時(shí)的編碼效率,來達(dá)到一些便利,脫離框架層面,編寫業(yè)務(wù)代碼時(shí),不得使用。
在 ASP.NET Core 中,自帶的容器是不支持屬性注入的,但是可以通過替換容器,如:Autofac 等來實(shí)現(xiàn)。今天我分享的方法不是使用替換容器,而是通過幾行代碼來實(shí)現(xiàn)屬性注入的效果,我稱為“偽屬性注入”。
三.屬性注入解決的痛點(diǎn)
以下介紹的痛點(diǎn)是我在實(shí)際編碼過程中遇到的一些,如果還有其他的,歡迎在評(píng)論和我交流
我所遇到的痛點(diǎn),我歸納為三條:
1.減少常用的類型的重復(fù)注入代碼,使構(gòu)造方法看起來更為簡潔,提高閱讀性。
2.減少或消除因構(gòu)造方法注入造成子類繼承后的 base 調(diào)用鏈。
3.并非是滿足第一條或第二條就需要使用屬性注入來解決,只有當(dāng)?shù)谝弧⒍l發(fā)生的情況到達(dá)一定的數(shù)量。
第一條:
以日志 ILogger<T>
為例,我們?cè)?Controller 或者 應(yīng)用服務(wù)層(Application Service)等編寫業(yè)務(wù)的地方可能會(huì)常用,那么我們可能會(huì)在大部分的 Controller 或者 Application Service 的構(gòu)造方法里寫一句注入,例:
這里只是以日志來舉例,我們還能遇到和日志這種相同的類型,每個(gè) Controller 等都要注入一堆這種共同的類型,代碼編寫起來也比較麻煩,如果多了以后還影響代碼閱讀。
有何解決辦法,那就是定義一個(gè)基類,然后通過屬性提供給子類,以 Controller 為例:
第二條:
在上面的Controller基類注入 ILogger,然后設(shè)置了 Logger 屬性,這樣子類就可以使用 Logger 屬性來使用日志。
這樣做每次都要調(diào)用 base 將依賴對(duì)象傳遞給基類,如果繼承關(guān)系有多層,將會(huì)造成更大的影響。
注意:本文演示只以日志來舉例,如果只有一個(gè)ILogger我覺得還可以忍受,實(shí)際情況中并非只有一個(gè),比如本地化等等。博主不提倡有上面演示情況的就用屬性注入,當(dāng)?shù)竭_(dá)一定數(shù)量才使用,比如在 Controller 或者應(yīng)用服務(wù)這種數(shù)量多的對(duì)象以及當(dāng)這些對(duì)象需要的共同的注入類型達(dá)到一定數(shù)量。
四. 偽屬性注入核心思想
依托于 ASP.NET Core 自帶的容器,在 Resolve Service 時(shí),為需要“屬性注入”的屬性進(jìn)行賦值,可以使用 自帶容器提供的 ImplementationFactory
來實(shí)現(xiàn)。
五. 為 Controller 實(shí)現(xiàn)偽屬性注入
Controller 的實(shí)現(xiàn)較為特殊,Controller 默認(rèn)是不會(huì)通過自帶容器來 Resolve&Activate 的,是通過MVC自身管理的,但是微軟提供了這樣的方法:
services.AddControllers().AddControllersAsServices();
可以通過調(diào)用 AddControllersAsServices()
方法來讓 Controller 使用自帶容器,其主要源代碼如下
根據(jù)第四小節(jié)的思想,我們需要 Controller Resolve 時(shí),來對(duì)屬性進(jìn)行賦值,那么我們需要改造 Controller 激活器。
定義 Controller 基類
Controller 繼承基類
改造 Controller 激活器
可以看到我們改造的代碼也就幾行。
替換默認(rèn) Controller 激活器
services.AddControllers().AddControllersAsServices(); services.Replace(ServiceDescriptor.Transient<IControllerActivator, XcServiceBasedControllerActivator>()); //替換默認(rèn) Controller 激活器
運(yùn)行測試
測試正常,如需其他屬性的“屬性注入”,參考日志這樣做就行了。
六. 為 Application Service 實(shí)現(xiàn)偽屬性注入
只是以 Application Service 來作為講解,同理可舉一反三到其他地方。Application Service 屬于領(lǐng)域驅(qū)動(dòng)分層架構(gòu)中的一層,如不了解,可自行查找資料。
定義應(yīng)用服務(wù)基類接口
public interface IAppService { ILogger Logger { get; set; } } public class AppService:IAppService { public ILogger Logger { get; set; } }
定義具體服務(wù),以 User 服務(wù)為例
public interface IUserAppService:IAppService { void Create(); } public class UserAppService : AppService,IUserAppService { public void Create() { Logger.LogInformation("來自 Application Service 的日志"); } }
定義特殊的注冊(cè)服務(wù)的方法,以便實(shí)現(xiàn) Resolve 為 Logger 賦值
public static class ServiceExtensions { public static IServiceCollection AddApplicationService<TService, TImpl>(this IServiceCollection services) where TService:IAppService where TImpl:AppService { services.AddApplicationService(typeof(TService), typeof(TImpl)); return services; } // 可以反射程序集調(diào)用此方法實(shí)現(xiàn)批量自動(dòng)注冊(cè)應(yīng)用服務(wù) public static IServiceCollection AddApplicationService(this IServiceCollection services, Type serviceType,Type implType) { services.AddTransient(serviceType, sp => { //獲取服務(wù)實(shí)現(xiàn)的實(shí)例 var implInstance = ActivatorUtilities.CreateInstance(sp, implType); ; if (implInstance is AppService obj) { //為 Logger 賦值 obj.Logger= sp.GetRequiredService<ILoggerFactory>().CreateLogger(implType); } return implInstance; }); return services; }
注冊(cè)測試服務(wù)
Controller 注入測試服務(wù)
運(yùn)行測試
七.結(jié)束
其實(shí)到本文寫完,我都在想,要不要封裝一個(gè)組件,發(fā)布到 Nuget 來方便的使用文中我所描述的“偽屬性注入”,最后反復(fù)想了想,還是覺得不做。如果要使用完全的屬性注入可以替換使用第三方容器,本文所述旨在不想引入第三方容器,且想在部分地方來達(dá)到屬性注入的效果,因?yàn)閷傩宰⑷脒@個(gè)東西也不推薦大范圍使用。
本文來源于我在工作中的一些靈感總結(jié),我在看 ControllerActivator
源碼時(shí)的突發(fā)奇想,最近工作雖然忙,但是知識(shí)確實(shí)積攢了不少,在后面與大家一一分享。
姊妹篇:ASP.NET Core 奇淫技巧之動(dòng)態(tài)WebApi
到此這篇關(guān)于ASP.NET Core 奇淫技巧之偽屬性注入的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)ASP.NET Core 偽屬性注入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
作者:曉晨Master(李志強(qiáng))
文章鏈接:https://www.cnblogs.com/stulzq/p/12610026.html
- 詳解ASP.NET Core實(shí)現(xiàn)強(qiáng)類型Configuration讀取配置數(shù)據(jù)
- 詳解asp.net core 依賴注入
- ASP.NET Core 過濾器中使用依賴注入知識(shí)點(diǎn)總結(jié)
- ASP.NET Core依賴注入系列教程之控制反轉(zhuǎn)(IoC)
- ASP.NET Core依賴注入系列教程之服務(wù)的注冊(cè)與提供
- ASP.NET Core DI手動(dòng)獲取注入對(duì)象的方法
- 詳解ASP.NET Core 中的框架級(jí)依賴注入
- 詳解ASP.NET Core 在 JSON 文件中配置依賴注入
- 如何在ASP.NET Core 的任意類中注入Configuration
相關(guān)文章
ASP.net 動(dòng)態(tài)加載控件時(shí)一些問題的總結(jié)
經(jīng)常見到有人說在ASP.net中不要使用動(dòng)態(tài)控件,我想主要的原因在于使用動(dòng)態(tài)控件會(huì)帶來一些問題,在做項(xiàng)目的過程中,我將由動(dòng)態(tài)加載控件引發(fā)的總是作了一個(gè)小小的總結(jié).2009-04-04Asp.net管理信息系統(tǒng)中數(shù)據(jù)統(tǒng)計(jì)功能的實(shí)現(xiàn)方法
這篇文章主要介紹了Asp.net管理信息系統(tǒng)中數(shù)據(jù)統(tǒng)計(jì)功能的實(shí)現(xiàn)方法,需要的朋友可以參考下2017-07-07讀取XML并綁定至RadioButtonList實(shí)現(xiàn)思路及演示動(dòng)畫
讀取XML的文檔,可以使用System.Data.DataSet類別中的ReadXml()方法,在aspx網(wǎng)頁上拉一個(gè)RadioButtonList控件,用來顯示XML的數(shù)據(jù),接下來,用DataSet去讀取剛才寫好的獲取XML文件的屬性,即可完成2013-01-01ASP.NET 文件斷點(diǎn)續(xù)傳實(shí)現(xiàn)代碼
在文件下載的時(shí)候,使用斷點(diǎn)續(xù)傳可以將上次未下載完成的文件繼續(xù)下載,該功能在開發(fā)文件下載的時(shí)候非常重要。這里我將介紹一種比較簡單的斷點(diǎn)續(xù)傳功能的實(shí)現(xiàn)方法,僅供初學(xué)者參考使用2012-06-06asp.net ajaxControlToolkit ValidatorCalloutExtender的簡單用法
今天偶爾用到這個(gè)控件,簡單記錄下~~~~2008-11-11ASP.NET MVC 中實(shí)現(xiàn)基于角色的權(quán)限控制的處理方法
在ASP.NET MVC中,通過使用其所提供的內(nèi)置2013-03-03