天天看點

ASP.NET Core 奇淫技巧之僞屬性注入

一.前言

開局先唠嗑一下,許久未曾更新部落格,一直在調整自己的狀态,去年是我的本命年,或許是應驗了本命年的多災多難,過得十分不順,不論是生活上還是工作上。還好當我度過了所謂的本命年後,許多事情都在慢慢變好,我将會開始恢複更新部落格,争取恢複到以前的速度上(因為工作比較忙,是以這個過程可能需要一段時間)。

二.關于屬性注入

說到屬性注入,我們就不得不提一下 DI(Dependency Injection),即依賴注入,用過 ASP.NET Core 的同學相信對這個詞不會陌生。ASP.NET Core 自帶了一個IOC容器,且程式運作也是基于這個容器建立起來的,在 Startup 裡的

ConfigureServices

方法就是向容器注冊類型。最直白的講,我們在 ASP.NET Core 中,想使用某個類型的時候可以不用自己去 new,可以由容器通過構造方法來注入具體的實作類型,而我們一般在構造方法上定義的依賴類型都是接口,而不是去依賴具體的實作,這裡就展現了 SOLID 原則中的依賴倒置原則(DIP)。這也是IOC(Inversion of Control),即控制反轉,不直接依賴具體實作,将依賴交給容器去控制。上述幾者是具有一定的關聯關系的,DIP 是一種軟體設計原則,IOC 是 DIP 的具體實作方式,DI 是 IOC 的一種實作方式。

在依賴注入時,我們最常用的便是通過構造方法注入,還有另一種方式那便是屬性注入。

關于屬性注入,如果在網上搜尋,大部分内容都是不推薦使用,或者說慎重使用的,因為屬性注入會造成類型的依賴關系隐藏,測試不友好等,我也同意這種說法,屬性注入可以使用,但是要謹慎,不能盲目使用。我的原則:在封裝架構(搭架子)時可以使用,但不能大範圍使用,隻有必須使用屬性注入來達到效果的地方才會使用,用來提高使用架構時的編碼效率,來達到一些便利,脫離架構層面,編寫業務代碼時,不得使用。

在 ASP.NET Core 中,自帶的容器是不支援屬性注入的,但是可以通過替換容器,如:Autofac 等來實作。今天我分享的方法不是使用替換容器,而是通過幾行代碼來實作屬性注入的效果,我稱為“僞屬性注入”。

三.屬性注入解決的痛點

以下介紹的痛點是我在實際編碼過程中遇到的一些,如果還有其他的,歡迎在評論和我交流

我所遇到的痛點,我歸納為三條:

1.減少常用的類型的重複注入代碼,使構造方法看起來更為簡潔,提高閱讀性。

2.減少或消除因構造方法注入造成子類繼承後的 base 調用鍊。

3.并非是滿足第一條或第二條就需要使用屬性注入來解決,隻有當第一、二條發生的情況到達一定的數量。

第一條:

以日志

ILogger<T>

為例,我們在 Controller 或者 應用服務層(Application Service)等編寫業務的地方可能會常用,那麼我們可能會在大部分的 Controller 或者 Application Service 的構造方法裡寫一句注入,例:

ASP.NET Core 奇淫技巧之僞屬性注入

這裡隻是以日志來舉例,我們還能遇到和日志這種相同的類型,每個 Controller 等都要注入一堆這種共同的類型,代碼編寫起來也比較麻煩,如果多了以後還影響代碼閱讀。

有何解決辦法,那就是定義一個基類,然後通過屬性提供給子類,以 Controller 為例:

ASP.NET Core 奇淫技巧之僞屬性注入

第二條:

在上面的Controller基類注入 ILogger,然後設定了 Logger 屬性,這樣子類就可以使用 Logger 屬性來使用日志。

這樣做每次都要調用 base 将依賴對象傳遞給基類,如果繼承關系有多層,将會造成更大的影響。

ASP.NET Core 奇淫技巧之僞屬性注入
注意:本文示範隻以日志來舉例,如果隻有一個ILogger我覺得還可以忍受,實際情況中并非隻有一個,比如本地化等等。部落客不提倡有上面示範情況的就用屬性注入,當到達一定數量才使用,比如在 Controller 或者應用服務這種數量多的對象以及當這些對象需要的共同的注入類型達到一定數量。

四. 僞屬性注入核心思想

依托于 ASP.NET Core 自帶的容器,在 Resolve Service 時,為需要“屬性注入”的屬性進行指派,可以使用 自帶容器提供的

ImplementationFactory

來實作。

五. 為 Controller 實作僞屬性注入

Controller 的實作較為特殊,Controller 預設是不會通過自帶容器來 Resolve&Activate 的,是通過MVC自身管理的,但是微軟提供了這樣的方法:

services.AddControllers().AddControllersAsServices();
           

可以通過調用

AddControllersAsServices()

方法來讓 Controller 使用自帶容器,其主要源代碼如下

ASP.NET Core 奇淫技巧之僞屬性注入

根據第四小節的思想,我們需要 Controller Resolve 時,來對屬性進行指派,那麼我們需要改造 Controller 激活器。

  1. 定義 Controller 基類
ASP.NET Core 奇淫技巧之僞屬性注入
  1. Controller 繼承基類
ASP.NET Core 奇淫技巧之僞屬性注入
  1. 改造 Controller 激活器
ASP.NET Core 奇淫技巧之僞屬性注入

可以看到我們改造的代碼也就幾行。

  1. 替換預設 Controller 激活器
services.AddControllers().AddControllersAsServices();
services.Replace(ServiceDescriptor.Transient<IControllerActivator, XcServiceBasedControllerActivator>()); //替換預設 Controller 激活器
           
  1. 運作測試
ASP.NET Core 奇淫技巧之僞屬性注入

測試正常,如需其他屬性的“屬性注入”,參考日志這樣做就行了。

六. 為 Application Service 實作僞屬性注入

隻是以 Application Service 來作為講解,同理可舉一反三到其他地方。Application Service 屬于領域驅動分層架構中的一層,如不了解,可自行查找資料。
  1. 定義應用服務基類接口
public interface IAppService
{
    ILogger Logger { get; set; }
}

public class AppService:IAppService
{
    public ILogger Logger { get; set; }
}
           
  1. 定義具體服務,以 User 服務為例
public interface IUserAppService:IAppService
{
    void Create();
}

public class UserAppService : AppService,IUserAppService
{
    public void Create()
    {
        Logger.LogInformation("來自 Application Service 的日志");
    }
}
           
  1. 定義特殊的注冊服務的方法,以便實作 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;
    }
    // 可以反射程式集調用此方法實作批量自動注冊應用服務
    public static IServiceCollection AddApplicationService(this IServiceCollection services, Type serviceType,Type implType)
    {
        services.AddTransient(serviceType, sp =>
        {
            //擷取服務實作的執行個體
            var implInstance = ActivatorUtilities.CreateInstance(sp, implType); ;

            if (implInstance is AppService obj)
            {
                //為 Logger 指派
                obj.Logger= sp.GetRequiredService<ILoggerFactory>().CreateLogger(implType);
            }
            
            return implInstance;
        });
        return services;
    }
           
  1. 注冊測試服務
ASP.NET Core 奇淫技巧之僞屬性注入
  1. Controller 注入測試服務
ASP.NET Core 奇淫技巧之僞屬性注入
ASP.NET Core 奇淫技巧之僞屬性注入

七.結束

其實到本文寫完,我都在想,要不要封裝一個元件,釋出到 Nuget 來友善的使用文中我所描述的“僞屬性注入”,最後反複想了想,還是覺得不做。如果要使用完全的屬性注入可以替換使用第三方容器,本文所述旨在不想引入第三方容器,且想在部分地方來達到屬性注入的效果,因為屬性注入這個東西也不推薦大範圍使用。

本文來源于我在工作中的一些靈感總結,我在看

ControllerActivator

源碼時的突發奇想,最近工作雖然忙,但是知識确實積攢了不少,在後面與大家一一分享。

姊妹篇:ASP.NET Core 奇淫技巧之動态WebApi

目前學習.NET Core 最好的教程 .NET Core 官方教程 ASP.NET Core 官方教程

.NET Core 交流群:923036995  歡迎加群交流

如果您認為這篇文章還不錯或者有所收獲,您可以點選右下角的【推薦】支援,或請我喝杯咖啡【贊賞】,這将是我繼續寫作,分享的最大動力!

作者:曉晨Master(李志強)

聲明:原創部落格請在轉載時保留原文連結或者在文章開頭加上本人部落格位址,如發現錯誤,歡迎批評指正。凡是轉載于本人的文章,不能設定打賞功能,如有特殊需求請與本人聯系!