D-11 注入 ? autofac ? dependency injection

關於注入

基於關注點分離所以要做到依賴注入,相依於介面而非實做,如此要抽換實作的內容就顯得比較方便,簡單舉例來說今天要用手機寄送驗證碼要改成Email寄送驗證碼在處理方式上只要將注入的元件置換掉即可而不需要修改期他的程式部分,不過該怎麼做呢,今天用autofac來告訴你。

autofac

「前輩,我們的元件越來越多了,這樣要一個一個注入很麻煩耶,而且新專案如果有需要用到元件的話可能會忘記注入耶。」
一大早大頭跟老K提出新的問題,小光這時聽到後對於一個東西很陌生所以就這樣問了一句。
「大頭前輩甚麼是注入阿。」
聽到小光這樣問,老K跟大頭都轉頭看向他,這是老K突然想到一件事似的微微的笑著,接下來他對大頭這麼說。
「大頭,你帶小光學習一下甚麼是注入,然後你跟他一起研究一下autofac這個套件。」
所以小光的這一天就從注入開始學習autofac。

dependency injection

如同前言所說關於依賴注入就是指說為了要分離關注點,所以我們不是直接依賴於類別,而是透過控制反轉,將原本類別直接new出來的field透過建構子注入的方式傳入到物件中讓外部決定要傳入的實體是甚麼,然後再藉由相依於介面而達到物件不用在乎實體是甚麼,只要專心在自身的邏輯上即可,而整體應用程式再來決定要注入的實作是甚麼來達到軟體開發的多樣性。

所以如同上面所說的,在dotnetcore要如何做到相依性插入呢,所以接下來就看看以下說明吧,首先先來說明類別要如何使用,之後再進階到如何管理注入。

類別的實作

如同前面所說,類別要直接相依於介面取代相依於類別,所以我們要先宣告介面,而介面的宣告如下所示。

public interface ISendEmailService
{
    Task Send(string message);
}

當介面宣告好了之後對於Controller或是其他的類別就直接使用這個介面,而非實作的類別如下所示。

[ApiController]
[Route("api/[controller]")]
public class EmailController : ControllerBase
{
    private readonly ISendEmailService _sendService = null;
    public EmailController (ISendEmailService sendService)
    {
        _sendService = sendService;
    }

    [HttpPost("Send")]
    public async Task<Result> Send(string message)
    {
        var result = new Result();
        await _sendService.Send(message);
        result.IsSuccess = true;
        return result;
    }
}

這裡要注意,注入的方式有兩種一種是屬性注入另一種是建構子注入,然而筆者常用的是建構子注入如此可以在前期就知道整個專案是否有正常運作,而不會等到執行時期才發現沒注入,又或者在過程中屬性注入的資料被改變,所以類別的部分處理完了接下來要介紹怎麼要注入了。

dotnetcore的注入

dotnetcore的注入很單純的就是在Startup.ConfigureServices做處理即可,所以要做甚麼處理請看以下說明。

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<ISendEmailService, EmailService1>();
    services.AddSingleton<ISendEmailService, EmailService2>();
    services.AddTransient<ISendEmailService, EmailService3>();
}

如此可以完成注入的動作了,至於AddScopedAddSingletonAddTransient的差別請見D-18 生命週期 ? request pipeline ? di lifecycle這邊不再贅述了,不過如同大頭所述如果今天需要注入的元件很多則注入的程式碼會寫很多很雜,再來如果使用到特殊元件需要注入期他元件時難免會漏掉註冊注入的動作,所以我們使用autofac來解決這個問題。

autofac

這部分跟大家介紹如何使用autofac來幫助大家解決大頭的問題,相信在.Net framework的讀者們如果有用過di的話應該對這套件不陌生,但是也應該有人沒用過,所以今天就跟大家介紹如何使用autofac以及如何讓原生的di跟autofac做搭配。

autofac的設定

在告訴大家怎麼使用之前要先跟大家說明如何設定環境,首先要安裝以下套件。

dotnet add package Autofac.Extensions.DependencyInjection

如此就可以安裝autfac以及autofac的擴充套件,若是函式庫需要寫模組的話可以只安裝下列套件即可。

dotnet add package Autofac

autofac的注入

在設定好環境後接下來要跟大家介紹autofac注入的方式,以上述例子為例在autofac可以這樣設定,首先調整Startup.ConfigureServices

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    var builder = new ContainerBuilder();
    builder.Populate(services);
    _container = builder.Build();
    return new AutofacServiceProvider(_container);
}

還有Program.CreateHostBuilder要調整以下部分將Host替換成WebHost

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .
    .

如此就完成autofac的起手式,接下來再跟大家說明如何注入元件,這時一樣只要調整Startup.ConfigureServices

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    .
    builder.RegisterType<EmailService1>().As<ISendEmailService>().SingleInstance();
    builder.RegisterType<EmailService2>().As<ISendEmailService>().InstancePerRequest();
    builder.RegisterType<EmailService3>().As<ISendEmailService>().InstancePerLifetimeScope();
    builder.RegisterType<EmailService4>().As<ISendEmailService>().InstancePerDependency();
    .
}

以上最後的方法是元件的存續期間,至於他與原生的di的對照表可以參考lifetime mapping,不過這邊整理部分如下。

原生 autofac
services.AddTransient<,> InstancePerDependency()
services.AddScope<,> InstancePerLifetimeScope()
services.AddScope<,> InstancePerRequest()
services.AddSingleton<,> SingleInstance()

autofac還有許多生存期間的設定,但是這邊先不贅述待有心的讀者們再去觀看了,所以接下來進入如何處理模組注入以及大批注入。

模組及大批注入

大家還記得大頭的需求吧,所以這邊先處理第一個,如何快速的注入許多元件,autofac在注入時可以針對組件內的class來做注入他的注入方式如下。

builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
    .Where(t => t.Name.EndsWith("Service"))
    .AsImplementedInterfaces()
    .InstancePerDependency();

如此就可以把class是Service的元件給注入,AsImplementedInterfaces就是使用他的interface的就會被注入,這樣是不是不用寫那麼多注入的程式碼,接下來說明一下如何寫成模組讓相依的元件一次注入,首先說明模組怎麼注入請看下列說明。

builder.RegisterModule(new CustomModule());

這樣可以使用CustomModule把需要的元件一次注入進去,接下來下面例子說明如何寫模組。

public class CustomModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        // 處理注入的動作
    }
}

如此就不怕元件有缺少囉。

後記

今天用autofac來說明怎麼讓注入更便利,以及說明原生的相依性插入元件跟autofac的生存期間的對應,希望對大家的開發有幫助。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *