依赖注入在 dotnet core 中实现与使用:4. 集成 Autofac
本示例使用 .net core 5 rc-1 实现。
1. 添加 Nuget 包引用
使用 Autofac 当然要添加 Autofac 的 Nuget 包,主要涉及到两个:
- Autofac.Extensions.DependencyInjection 核心支持包
- Autofac.Extras.DynamicProxy2 AOP 动态代理支持
如果不需要动态代理的话,只需要添加第一个即可。
dotnet add package Autofac.Extensions.DependencyInjection
2. 配置 Autofac
首先需要需要配置 Autofac 的容器工厂。
由于需要使用 Autofac 的容器,所以在构建应用程序的时候,需要使用 Autofac 的服务工厂。主程序 Program 中的 CreateHostBuilder() 方法需要增加一行,修改之后如下所示:
public static IHostBuilder CreateHostBuilder (string[] args) =>
Host.CreateDefaultBuilder (args)
.UseServiceProviderFactory (new AutofacServiceProviderFactory ())
.ConfigureWebHostDefaults (webBuilder => {
webBuilder.UseStartup<Startup> ();
});
}
然后,需要在 Startup() 中配置服务注册。
Autofac 的服务工厂会在调用 ConfigureServices() 之后,自动调用名为 ConfigureContainer() 的方法,一般情况下,我们会在这个方法里面使用 Autofac 来注册服务。
在 Startup 文件中,添加如下的 ConfigureContainer() 方法。ContainerBuilder 是定义在命名空间 Autofac 中的,注意添加对该命名空间的引用。
using Autofac;
public void ConfigureContainer (ContainerBuilder builder) {
......
}
Autofac 提供了各种注册服务的方法,不是微软的 Addxxx() 方式,而是 Registerxxx() 方式。
例如,如果我们已经定义了一个 IDbService 接口,而它的实现类型是 DbService。那么,注册服务的形式如下所示:
// register type, and enable interceptor injection
builder.RegisterType<DbService> ().As<IDbService> ()
.InstancePerLifetimeScope ();
DbService 是注册在容器中的实现类型,而 As<IDbService> 是在容器中注册的类型。注入的时候需要使用这个接口类型。InstancePerLifetimeScope() 则是说明它的生命周期是 Scope 类型的。
可以看到,在 Autofac 中,使用链式调用的方式来完成服务注册。
3. 使用 Autofac Module 进行注册
Autofac 提供了一个名为 Module 的概念,它支持将一组相关的服务注册过程进行打包,以简化配置和部署。
Autofac 提供了名为 Autofac.IModule 接口,以及一个它的抽象实现类型 Autofac.Module。它的核心是 Load() 方法,用来完成服务的注册。我们可以重载它以实现自定义的服务注册,该方法的签名如下:
protected virtual void Load(
ContainerBuilder builder
)
可以看到该方法提供同样的 ContainerBuilder 参数来提供服务注册的支持。
这样的话,前面的服务注册可以转移到一个 Autofac 的 Module 中来。
我们可以定义一个服务注册类,如下所示:
using Autofac;
using Microsoft.AspNetCore.Mvc;
public class ServiceAutofacModule : Autofac.Module {
protected override void Load (ContainerBuilder builder) {
// register type, and enable interceptor injection
builder.RegisterType<DbService> ().As<IDbService> ()
.InstancePerLifetimeScope ();
}
}
然后,将 Startup() 中的 ConfigureContainer() 调整为如下形式,使用 Module 的方式完成服务注册。
public void ConfigureContainer (ContainerBuilder builder) {
// use autofac module
builder.RegisterModule<ServiceAutofacModule>();
}
Module 的使用详见:https://autofaccn.readthedocs.io/en/latest/configuration/modules.html
4. 常见的注册方式
1. 按照类型进行注册
// register type, and enable interceptor injection
builder.RegisterType<DbService> ().As<IDbService> ()
.InstancePerLifetimeScope ();
2. 按已经引用的程序集注册
var assembly = assembly.Load ("Domain.Services");
builder.registerAssemblyType (assembly)
.AsImplementedInterfaces ()
.InstancePerLifetimeScope ();
3. 注册程序集中的某些服务
下面的代码中,先取得了 ControllerBase 的类型,然后在当前程序集中查找所有派生自 ControllerBase 的 Api 控制器
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
5. 使用属性注入
Autofac 除了支持构造函数注入,还支持属性注入,属性注入会在构造函数注入之后进行。
必须要注意的是,必须在使用属性注入的服务上进行声明,
例如,如果 DbService 需要支持属性注入,那么需要在注册该服务的时候进行声明。
builder.RegisterType<DbService> ().As<IDbService> ()
.PropertiesAutowired()
.InstancePerLifetimeScope ();
ASP.NET Core 中,对控制器进行属性注入的特殊处理
默认情况下,ASP.NET Core 对于控制器并不是从容器中创建的,所以如果你检查容器中的注册,是看不到控制器的注册的。
为了支持属性注入,需要让 ASP.NET Core 将控制器也注册到容器中。这可以在 AddControllers() 方法之后,调用 AddControllersAsServices() 来实现。
public void ConfigureServices (IServiceCollection services) {
services.AddControllers()
.AddControllersAsServices();
}
然后,我们需要对控制器添加支持属性注入的声明。
既可以针对单个的控制器类
// make property autowire at one controller
builder.RegisterType<WeatherForecastController>()
.PropertiesAutowired();
也可以针对所有的控制器。
// make property autowire at all api controller
var controllerBaseType = typeof (ControllerBase);
builder.RegisterAssemblyTypes (typeof (Program).Assembly)
.Where (t => controllerBaseType.IsAssignableFrom (t) &&
t != controllerBaseType)
.PropertiesAutowired ();
6. 使用 AOP 动态代理
使用 AOP 需要如下的 4 个步骤。
1. 定义拦截器
拦截器的接口 IInterceptor 定义在命名空间 Castle.DynamicProxy 中,需要注意的是,它需要添加对 NuGet 包 Autofac.Extras.DynamicProxy 的引用。
dotnet add package Autofac.Extras.DynamicProxy
实现 IInterceptor 接口。
using Castle.DynamicProxy;
using System;
public class DbServiceInterceptor:IInterceptor
{
public virtual void Intercept(IInvocation invocation)
{
Console.WriteLine($"{DateTime.Now}: Before method execting. ");
invocation.Proceed();
Console.WriteLine($"{DateTime.Now}: After method exected.");
}
}
2. 注册拦截器
拦截器也同样需要注册到容器中。
// register interceptor
builder.RegisterType<DbServiceInterceptor> ();
3. 启用拦截器
需要支持拦截器的服务需要启用拦截器,然后才能使用拦截器。
// register type, and enable interceptor injection
builder.RegisterType<DbService> ().As<IDbService> ()
.EnableInterfaceInterceptors ()
.InstancePerLifetimeScope ();
可以使用 EnableInterfaceInterceptors() 或者 EnableClassInterceptors() 扩展方法来启用拦截器。
EnableInterfaceInterceptors() 创建接口代理来执行拦截,而 EnableClassInterceptors() 则创建目标组件的子类来执行拦截。
4. 使用拦截器
第一种方式是在使用拦截器的服务上,通过特性来声明使用的拦截器。
using Autofac.Extras.DynamicProxy;
using Castle.DynamicProxy;
[Intercept (typeof (DbServiceInterceptor))]
public class DbService : IDbService {
public string Say () {
return "Hello";
}
}
当使用特性来关联拦截器的时候,不需要在注册服务的时候指定拦截器。你只需要启用,实际的拦截器将被自动发现。
第二种方式是在注册服务的时候指定,使用 InterceptedBy() 扩展方法。
builder.RegisterType<SomeType>()
.EnableClassInterceptors()
.InterceptedBy(typeof(CallLogger));
注意:
- 使用公共接口
- 类拦截要求被拦截的方法是虚方法,因为使用了子类代理技术。
- 通过表达式创建的服务,或者使用实例注册的服务,不能使用子类方式代理,此时,要使用接口代理。
- 要使用接口代理,服务必须仅仅通过接口提供服务,为了最佳的性能,所有此类服务接口必须是注册的一部分,例如使用 .As 子句。
- 如果通过 EnableClassInterceptors() 使用了类拦截,则避免使用构造函数选择器 UsingConstructor()。在使用类拦截的时候,会为代理类生成新的构造函数以获取你希望使用的拦截器。如果你使用了 UsingConstructor(),就会跳过此逻辑。导致拦截器不能被使用。
已知问题:
- 同步方法拦截。Castle 拦截器仅仅支持同步方法拦截。不支持显式的 async/await 方法。但是,async/await 是 Task 的语法糖,你可以在拦截器中使用 Task 和 ContinueWith() 之类的方法。 This issue 展示了用法。另外,这些助手类 也使得 async 工作更容易一点。
- Castle.Core 版本问题。
参考资料
- Autofac 官网
- Autofac Document
- Autofac at GitHub
- Sample Project
- Aspect Oriented Programming (AOP) in .NET Core and C# using AutoFac and DynamicProxy
依赖注入在 dotnet core 中实现与使用:4. 集成 Autofac的更多相关文章
- 依赖注入在 dotnet core 中实现与使用:1 基本概念
关于 Microsoft Extension: DependencyInjection 的介绍已经很多,但是多数偏重于实现原理和一些特定的实现场景.作为 dotnet core 的核心基石,这里准备全 ...
- 依赖注入在 dotnet core 中实现与使用:2 使用 Extensions DependencyInjection
既然是依赖注入容器,必然会涉及到服务的注册,获取服务实例,管理作用域,服务注入这四个方面. 服务注册涉及如何将我们的定义的服务注册到容器中.这通常是实际开发中使用容器的第一步,而容器本身通常是由框架来 ...
- 依赖注入在 dotnet core 中实现与使用:3 使用 Lazy<T> 延迟实例化
有些对象我们并不想一开始就实例化,由于性能或者功能的考虑,希望等到使用的时候再实例化.考虑存在一个类 A, 它使用了依赖的类 B,在 A 中,只有某些不常用到的方法会涉及调用 B 中的方法,多数情况下 ...
- Dotnet Core中使用AutoMapper
官网:http://automapper.org/ 文档:https://automapper.readthedocs.io/en/latest/index.html GitHub:https://g ...
- ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入)
前言 本文主要是详解一下在ASP.NET Core中,自带的IOC容器相关的使用方式和注入类型的生命周期. 这里就不详细的赘述IOC是什么 以及DI是什么了.. emm..不懂的可以自行百度. 目录 ...
- 依赖注入[8]: .NET Core DI框架[服务消费]
包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IServiceProvider对象.当需要消费某个服务实例的时候,我们只需要指定服务类型调用IServicePr ...
- 依赖注入[7]: .NET Core DI框架[服务注册]
包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IServiceProvider对象.服务注册就是创建出现相应的ServiceDescriptor对象并将其添加到 ...
- 依赖注入[6]: .NET Core DI框架[编程体验]
毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动时构建请求处理管道过程中,以及利用该管道处理每个请求过程中使用到的服务对象均来源于DI容器.该DI容器不仅为A ...
- .NET CORE学习笔记系列(2)——依赖注入[7]: .NET Core DI框架[服务注册]
原文https://www.cnblogs.com/artech/p/net-core-di-07.html 包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IS ...
随机推荐
- ssm框架(Spring Springmvc Mybatis框架)整合及案例增删改查
三大框架介绍 ssm框架是由Spring springmvc和Mybatis共同组成的框架.Spring和Springmvc都是spring公司开发的,因此他们之间不需要整合.也可以说是无缝整合.my ...
- gson 处理null
1.定义null处理类 class StringConverter : JsonSerializer<String?>, JsonDeserializer<String?> { ...
- js扩展运算符(spread)三个点(...)
常见用法: 1.该运算符主要用于函数调用. function push(array, ...items) { array.push(...items); } function add(x, y) { ...
- 面试:为了进阿里,又把并发CAS(Compare and Swap)实现重新精读一遍
该系列文章已收录在公众号[Ccww技术博客],原创技术文章第一时间推出 前言 在面试中,并发线程安全提问必然是不会缺少的,那基础的CAS原理也必须了解,这样在面试中才能加分,那来看看面试可能会问那些问 ...
- 类文件的结构、JVM 的类加载过程、类加载机制、类加载器、双亲委派模型
一.类文件的结构 我们都知道,各种不同平台的虚拟机,都支持 "字节码 Byte Code" 这种程序存储格式,这构成了 Java 平台无关性的基石.甚至现在平台无关性也开始演变出 ...
- 05_Python的文件操作
1.文件操作概述 # 文件是用于数据存储的单位通常用来长期存储设置,文件中的数据是以字节为单位进行顺序存储的 1.打开文件: f = open("xxx") 或 with ...
- Spring的循环依赖,学就完事了【附源码】
目录 啥是循环依赖? Spring可以解决循环依赖的条件 Spring如何去解决循环依赖 SpringBean的创建流程 Spring维护的三级缓存 getSingleton getSingleton ...
- windows软件介绍
基础软件 txt 阅读 NotePad++,方便阅读代码,支持列复制. 全局搜索 Everything 截图 Snipaste 读取图片 Picase.PureRef 其他 置顶窗口 DeskPins ...
- 菜鸟电子面单对接技术方案(link)
一.背景 快递业务日新月异,收发快递是生活中不可缺少的一部分了,特别是做微商的商家,每天发送大量的快递.填写快递单已经成为过去式,快递小哥上门收件的时候,都使用手持的中端设备,再也不用客户填写快递单了 ...
- GaussDB(DWS)应用实战:对被视图引用的表进行DDL操作
摘要:GaussDB(DWS)是从Postgres演进过来的,像Postgres一样,如果表被视图引用的话,特定场景下,部分DDL操作是不能直接执行的. 背景说明 GaussDB(DWS)是从Post ...