造轮子之自动依赖注入
在我们造轮子的起初,基建非常重要,而依赖注入是我们使用频率最高的一项,频繁的手动注入太麻烦,所以我们来实现一下自动化注入。
技术选型
在ASP.NET Core中,有两种常见的依赖注入方式:原生依赖注入和三方依赖注入。
原生依赖注入
ASP.NET Core提供了一个内置的依赖注入容器,可以用于管理应用程序中的依赖关系。原生依赖注入是ASP.NET Core框架的一部分,因此不需要额外的库或包。它提供了基本的依赖注入功能,可以满足大多数应用程序的需求。
原生依赖注入的优点:
轻量级:原生依赖注入是框架的一部分,因此不需要额外的库或包。
易于使用:它提供了简单的API,可以轻松地注册和解析依赖项。
集成性:由于是框架的一部分,原生依赖注入与ASP.NET Core的其他功能集成得很好。
原生依赖注入的缺点:
功能相对较少:原生依赖注入提供了基本的依赖注入功能,但在一些高级场景下可能不够灵活。
缺乏某些高级功能:例如,原生依赖注入不支持属性注入或命名解析等高级功能。
三方依赖注入
ASP.NET Core也支持使用第三方依赖注入容器,例如Autofac、Ninject、Unity等。这些容器提供了更多的功能和灵活性,可以满足更复杂的依赖注入需求。
三方依赖注入的优点:
功能丰富:第三方容器通常提供了更多的功能,例如属性注入、生命周期管理、条件注册等。
灵活性:使用第三方容器可以更好地控制依赖注入的行为和配置。
可扩展性:第三方容器通常提供了扩展机制,可以轻松地集成自定义解析逻辑或扩展功能。
三方依赖注入的缺点:
学习曲线:使用第三方容器可能需要一些额外的学习和配置成本。
引入外部依赖:使用第三方容器会引入额外的依赖项,增加了应用程序的复杂性。
选择使用原生依赖注入还是三方依赖注入取决于具体的需求和偏好。对于简单的应用程序,原生依赖注入通常已经足够。对于复杂的应用程序或需要更高级功能的情况,可以考虑使用第三方依赖注入容器。
既然我们需要做一个比较灵活的依赖注入,那么就选择三方的组件更合适,这里我们选用autofac。
生命周期接口
依赖注入对应有不同的生命周期,我们按照官方三种生命周期创建三个生命周期接口。
分别是
ITransientDependency 瞬态生命周期接口
IScopeDependency 范围生命周期接口
ISingletonDependency 单例生命周期接口
这些接口的定义是为了我们后续做自动化注入用的。
集成Autofac
安装Autofac的NUGET包。
分别是
Autofac.Extensions.DependencyInjection
AutoMapper.Extensions.Microsoft.DependencyInjection
替换asp.net core原生依赖注入容器
在Program中添加下面代码
using Autofac;
using Autofac.Extensions.DependencyInjection;
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
实现批量自动注入
在Autofac中有许多的注入方式,其中RegisterModule可以更方便的封装我们的注册依赖注入逻辑规则。
首先创建一个WheelAutofacModule,继承Autofac.Moudle,并重写Load方法。
using Autofac;
using Autofac.Core;
using Microsoft.AspNetCore.Mvc;
using System.Reflection;
using Wheel.DependencyInjection;
using Wheel.Domain;
using Wheel.EntityFrameworkCore;
using Module = Autofac.Module;
namespace Wheel
{
public class WheelAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
//把服务的注入规则写在这里
var abs = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
.Where(x => !x.Contains("Microsoft.") && !x.Contains("System."))
.Select(x => Assembly.Load(AssemblyName.GetAssemblyName(x))).ToArray();
builder.RegisterAssemblyTypes(abs)
.Where(t => typeof(ITransientDependency).IsAssignableFrom(t))
.AsImplementedInterfaces()
.AsSelf()
.PropertiesAutowired()
.InstancePerDependency(); //瞬态
builder.RegisterAssemblyTypes(abs)
.Where(t => typeof(IScopeDependency).IsAssignableFrom(t))
.AsImplementedInterfaces()
.AsSelf()
.PropertiesAutowired()
.InstancePerLifetimeScope(); //范围
builder.RegisterAssemblyTypes(abs)
.Where(t => typeof(ISingletonDependency).IsAssignableFrom(t))
.AsImplementedInterfaces()
.AsSelf()
.PropertiesAutowired()
.SingleInstance(); //单例.
// 获取所有控制器类型并使用属性注入
var controllerBaseType = typeof(ControllerBase);
builder.RegisterAssemblyTypes(abs)
.Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
.PropertiesAutowired();
}
}
}
既然我们需要批量切自动化注入,那么Autofac中RegisterAssemblyTypes根据程序集注册的方法就非常契合。
首先我们需要通过反射获取所有的dll程序集(可以加条件提前过滤已知不需要加载的程序集)。
接下来就是RegisterAssemblyTypes加载程序集,并且按照继承不同生命周期接口去注册不同的服务。
这里注意的是,如果需要使用属性注入,则需要添加PropertiesAutowired()方法。
实现WheelAutofacModule之后,我们需要在ContainerBuilder中注册一下我们的Module。
在Program中添加代码:
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterModule<WheelAutofacModule>();
});
所有代码加起来不到100行,这样就完成了我们自动依赖注入的所有步骤了。
在后续开发中,我们所有需要注册依赖注入的服务只需要按需继承三个生命周期的接口即可。
可能有人会问使用了Autofac之后是否必须所有的服务都必须用Autofac的方式去注册服务,不能使用原生的方式。这点大可不必担心,使用autofac后,我们依然可以使用原生的AddScope等方法手动去注入我们的服务,同样是生效的。
轮子仓库地址https://github.com/Wheel-Framework/Wheel
欢迎进群催更。