Autofac 依赖注入小知识
Autofac 依赖注入小知识
控制反转/依赖注入 IOC/DI
依赖接口而不依赖于实现,是面向对象的六大设计原则(SOLID)之一。即依赖倒置原则(Dependence Inversion Principle
)
生命周期分为三种,具体如下
Singleton
单例(全局唯一实例)Scoped
范围 (在同一个生命周期内是同一个实例)Transient
瞬时(每次请求都是一个新的实例)
使用说明
创建ASP.NET Core 3.0+
的项目,并安装Autofac
包
dotnet add package Autofac.Extensions.DependencyInjection
在Program 中Host主机指定 .UseServiceProviderFactory(new AutofacServiceProviderFactory())
.
UseServiceProviderFactory调用Autofac提供程序,附加到通用宿主机制。
public class Program
{
public static void Main(string[] args)
{
var host = Host.CreateDefaultBuilder(args)
+ .UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webHostBuilder => {
webHostBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup();
})
.Build();
host.Run();
}
}
在StartUp中配置
public class Startup
{
public Startup(IConfiguration configuration)
{
this.Configuration = configuration;
}
public IConfiguration Configuration { get; private set; }
+ public ILifetimeScope AutofacContainer { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
}
// ConfigureContainer is where you can register things directly
// with Autofac. This runs after ConfigureServices so the things
// here will override registrations made in ConfigureServices.
// Don't build the container; that gets done for you by the factory.
public void ConfigureContainer(ContainerBuilder builder)
{
// Register your own things directly with Autofac here. Don't
// call builder.Populate(), that happens in AutofacServiceProviderFactory
// for you.
+ builder.RegisterModule(new MyApplicationModule());
}
public void Configure(
IApplicationBuilder app,
ILoggerFactory loggerFactory)
{
+ this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
}
}
定义注入实现
public class MyApplicationModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType().As().SingleInstance();
}
}
- 注册泛型仓储
builder.RegisterGeneric(typeof(AuditBaseRepository<>)).As(typeof(IAuditBaseRepository<>)).InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(AuditBaseRepository<,>)).As(typeof(IAuditBaseRepository<,>)).InstancePerLifetimeScope();
- 一个接口多个实现,使用Named,区分、参数为字符串即可。
注册服务
builder.RegisterType().Named(typeof(IdentityServer4Service).Name).InstancePerLifetimeScope();
builder.RegisterType().Named(typeof(JwtTokenService).Name).InstancePerLifetimeScope();
根据Name获取哪个服务
private readonly ITokenService _tokenService;
public AccountController(IComponentContext componentContext, IConfiguration configuration)
{
bool isIdentityServer4 = configuration.GetSection("Service:IdentityServer4").Value?.ToBoolean() ?? false;
_tokenService = componentContext.ResolveNamed(isIdentityServer4 ? typeof(IdentityServer4Service).Name : typeof(JwtTokenService).Name);
}
可通过appsettings.json中配置,可决定是哪个服务
"Service": {
"IdentityServer4": false
}
- 基于接口的注入
AsImplementedInterfaces
Specifies that a type from a scanned assembly is registered as providing all of its implemented interfaces.
指定将扫描程序集中的类型注册为提供其所有实现的接口。
根据接口ITransientDependency
可以得到有哪些类继承了此接口,并判断是类,不是抽象类,不是泛型。
所有继承类接口的类,将以接口的方式自动注入实例。可直接使用接口即可。
- InstancePerDependency 瞬时 (每次请求都是一个新的实例)
- InstancePerLifetimeScope 范围(在同一个生命周期内是同一个实例)
- SingleInstance 单例(全局唯一实例)
public class DependencyModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
Assembly[] currentAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(r => r.FullName.Contains("LinCms.")).ToArray();
//每次调用,都会重新实例化对象;每次请求都创建一个新的对象;
Type transientDependency = typeof(ITransientDependency);
builder.RegisterAssemblyTypes(currentAssemblies)
.Where(t => transientDependency.GetTypeInfo().IsAssignableFrom(t) && t.IsClass && !t.IsAbstract && !t.IsGenericType)
.AsImplementedInterfaces().InstancePerDependency();
//同一个Lifetime生成的对象是同一个实例
Type scopeDependency = typeof(IScopedDependency);
builder.RegisterAssemblyTypes(currentAssemblies)
.Where(t => scopeDependency.GetTypeInfo().IsAssignableFrom(t) && t.IsClass && !t.IsAbstract && !t.IsGenericType)
.AsImplementedInterfaces().InstancePerLifetimeScope();
//单例模式,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;
Type singletonDependency = typeof(ISingletonDependency);
builder.RegisterAssemblyTypes(currentAssemblies)
.Where(t => singletonDependency.GetTypeInfo().IsAssignableFrom(t) && t.IsClass && !t.IsAbstract &&!t.IsGenericType)
.AsImplementedInterfaces().SingleInstance();
}
}
如果不写继承,如何批量注入呢。
1.类名有规则
2.基于特殊标签
3.继承接口。
- 类名有规则
比如仓储后缀,全是Repository
,其中Assembly
为仓储的实现所在程序集。将自动注入所有的仓储,仓储必须有接口。
Assembly assemblysRepository = Assembly.Load("LinCms.Infrastructure");
builder.RegisterAssemblyTypes(assemblysRepository)
.Where(a => a.Name.EndsWith("Repository"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
- 注入服务后就执行一段逻辑
builder.RegisterType().SingleInstance();
builder.RegisterBuildCallback(async (c) => await c.Resolve().StartAsync());
动态代理
dotnet add package Autofac.Extras.DynamicProxy
dotnet add package Castle.Core.AsyncInterceptor
- 服务注册
AOP+属性注入+以后缀为Service的服务实现,注入Scope 范围的生命周期+启用接口的拦截器。
- 使用
EnableInterfaceInterceptors
创建执行拦截的接口代理, - 使用
EnableClassInterceptors()
动态对子类进行重写, 执行virtual方法的拦截
builder.RegisterType();
builder.RegisterType();
List interceptorServiceTypes = new List()
{
typeof(UnitOfWorkInterceptor),
};
Assembly servicesDllFile = Assembly.Load("LinCms.Application");
builder.RegisterAssemblyTypes(servicesDllFile)
.Where(a => a.Name.EndsWith("Service") && !a.IsAbstract && !a.IsInterface && a.IsPublic)
.AsImplementedInterfaces()//接口注入
.InstancePerLifetimeScope()//生命周期:范围
.PropertiesAutowired()// 属性注入
.InterceptedBy(interceptorServiceTypes.ToArray())//声明拦截器
.EnableInterfaceInterceptors();//启用接口的拦截器。
这二个类,请参考如下代码
- 同步:UnitOfWorkInterceptor.cs https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/src/LinCms.Web/Middleware/UnitOfWorkInterceptor.cs
- 异步拦截:UnitOfWorkAsyncInterceptor.cs https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/src/LinCms.Web/Middleware/UnitOfWorkInterceptor.cs
Autofac.Extras.DynamicProxy
依赖Castle.Core,即只支持同步方法的拦截。
异步方法的拦截需要安装包:Castle.Core.AsyncInterceptor
。
- 异步方法,分为有/无返回值:
async Task RunAsync()
,asyn Task
RunAsync() - 同步方法:
void Run()
,Result Run()
同步拦截
1.定义拦截器
public class CallLogger : IInterceptor
{
TextWriter _output;
public CallLogger(TextWriter output)
{
_output = output;
}
public void Intercept(IInvocation invocation)
{
_output.Write("Calling method {0} with parameters {1}... ",
invocation.Method.Name,
string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()));
invocation.Proceed();
_output.WriteLine("Done: result was {0}.", invocation.ReturnValue);
}
}
2.注册拦截器。
// Named registration
builder.Register(c => new CallLogger(Console.Out))
.Named("log-calls");
// Typed registration
builder.Register(c => new CallLogger(Console.Out));
将拦截器与要拦截的类型 关联
[Intercept(typeof(CallLogger))]
public class First
{
public virtual int GetValue()
{
// Do some calculation and return a value
}
}
// This attribute will look for a NAMED
// interceptor registration:
[Intercept("log-calls")]
public class Second
{
public virtual int GetValue()
{
// Do some calculation and return a value
}
}
链接
- 官网 https://autofac.org/
- GitHub https://github.com/autofac/Autofac
- 文档 https://autofac.readthedocs.io/en/latest/