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的生存期间的对应,希望对大家的开发有帮助。


<<:  Day 29 整合宝石:【Lab】建构三层式云端架构 (EC2+VPC+S3+RDS+IAM) (上)

>>:  [2021铁人赛 Day19] General Skills 16

[Day11] 注册API – urls之专案资料夹

夥伴们大家好,时间过得好快,不知不觉已经到了铁人赛的第十一天,那麽今天来到我们的urls.py,这里...

人脸辨识-day29

在模型训练完成後,最终需要可以即时在侦测到人脸後辨识出来,可先设定当摄影机开启後的画面长宽与一些影像...

浅谈人机结合

人的科技文明发展始终来自於人性 在现今的科技与资讯发达的社会,人手一机已不再是奢望,连小小年纪的小朋...

Day 23 用户资料数据下载定义规划实作

下载打包规划是根据GDPR第20条和加州消费者隐私保护法CCPA (California Consu...

Day15 javascript 对象介绍

今天咱们要来谈的是JavaScript 对象,JavaScript 中的所有事物都是物件:不论是字串...