前言
为什么需要Autofac
.NET 提供了其轻量级的内置框架Microsoft.Extention.DependencyInjection
,用于实现依赖注入(DI)、控制反转(IoC)。但是这个框架的依赖注册(Register)比较繁琐,只能单个依赖地注入。Autofac是一个第三方的.NET Core 框架,该框架的一大优点就是可以结合反射,对一个程序集(Assembly)内的实现了接口的类进行分别的依赖注册(官网首页的Register Compenents部分有示例代码)。
为什么需要这篇文章
然而,国内社区中 .NET 相关的资料不太多,也比较落后。而且,Autofac 提供的中文文档的最新版本是4.0版本,落后于最新版本是7.0版本的英文文档。此外,英文文档似乎也没有更新,至少其中的集成应用中的ASP.NET Core 的介绍没有使用最新的语法。根据文档,我找到了Autofac 在Github 上的示例代码,结果我需要的部分都是3年前的版本。
这三年ASP.NET Core 做了许多的改变。C# 9.0(大约3年前)开始引入的顶级语句在后续的ASP.NET Core 中被应用,Startup.cs 文件不再需要,Program.cs 也有了很多的变化。3年前的实例代码中启动应用的方法,和现如今ASP.NET Core 提供的模板差别很大,无法用于学习。
入门
我认为官方文档中创建ContainerBuilder
对象、然后用其进行注册的方法在现今版本的ASP.NET Core 中是不可行的,因为我能看到的实例代码中,这一步都是在Startup.cs文件中的某一个方法内。而且,ASP.NET Core 似乎做了很多的更改,导致网上的许多过时的指导中需要用的方法、对象都不可用。
我在StackOverFlow 上看到了一篇问答,最后找到了合适的使用方法。原文链接:stackoverflow.com/questions/6... 。根据这篇这篇问答以及我的亲身实验,我总结出了以下指南(只是介绍下流程,具体的流程下的原理之类的我也不清楚)
简述流程
C#
using Autrofac;
using Autofac.Extentions.DependencyInjection;
var builder = new WebApplication(args);
// 重点
// 将Autofac 的依赖容器提供给WebApplication,
// 这样,通过Autofac 注册的组件就能够被注入
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
var app = builder.Build();
app.Run();
组件注册(Registering Components)
和过时的官方文档一样,注册组件的方法也是要通过ContainerBuilder
对象进行,但是我们不需要(或者说不能)去手动创建这个对象,而是交由AutofacServiceProviderFactory
类。首先看一下这个类的一个构造函数
csharp
/// <summary>
/// Initializes a new instance of the <see cref="AutofacServiceProviderFactory"/> class.
/// </summary>
/// <param name="configurationAction">Action on a <see cref="ContainerBuilder"/> that adds component registrations to the container..</param>
public AutofacServiceProviderFactory(Action<ContainerBuilder>? configurationAction = null)
可以看到,这个构造函数接受一个类型为Action<ContainerBuilder>
的委托,注释指出该委托用于像Autofac 依赖容器中添加组件。也就是说,我们要把文档中使用ContainerBuilder
对象进行组件注册的代码写在一个委托中,再将这个委托作为AutofacServiceProviderFactory
的构造函数的参数传入。
这样也有好处,我会在Program.cs文件中定一个签名与委托匹配的方法,在其中进行必要的依赖注入,也有利于增强顶级语句的可读性。
需要注意的是,据我观察,同时使用Autofac 以及内置的依赖注入进行注册都是有效的。也就是说,以上相当于把通过Autofac 注入的组件并入内置依赖注入框架的容器中(不太严谨地说)。个人感觉像DbContext
、控制器的注册,还是用内置依赖注入好。我使用Autfofac,主要也是为了补充内置框架的不足。
依赖注入
官方文档指出,可以使用隐式的依赖注入,我也有验证过,构造器注入的方式确实是隐式有效的。具体详见官方文档,目测没有太多改变。Resolving Services --- Autofac 7.0.0 documentation
我的示例
C#
using System.Reflection;
using Autofac;
using Autofac.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// controllers
builder.Services.AddControllers();
// Redis
builder.Services.AddSingleton<IDatabase>(_ => ConnectionMultiplexer
.Connect(builder.Configuration.GetConnectionString("Redis") ?? throw new Exception("Connection string 'Redis' not found."))
.GetDatabase()
);
// DbContext
builder.Services.AddDbContext<DiggingSoilContext>(options =>
options.UseMySQL(builder.Configuration.GetConnectionString("MySql") ?? throw new Exception("Connection string 'MySql' not found."))
);
// Use Autofac
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory(DependencyHelper));
var app = builder.Build();
app.Run();
void AutofacContainerBuilderConfiguration(ContainerBuilder containerBuilder)
{
// Services
containerBuilder.RegisterAssemblyTypes(Assembly.Load("MyProject.Services"))
.Where(t => t.IsClass && t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.SingleInstance();
// Repos
containerBuilder.RegisterAssemblyTypes(Assembly.Load("MyProject.Dal"))
.Where(t => t.IsClass && t.Name.EndsWith("Repo"))
.AsImplementedInterfaces()
.SingleInstance();
}
如有错误,欢迎指正