前言
本文主要是详解一下在 ASP.NET Core 中, 自带的 IOC 容器相关的使用方式和注入类型的生命周期.
这里就不详细的赘述 IOC 是什么 以及 DI 是什么了.. emm.. 不懂的可以自行百度.
目录
ASP.NET Core 中使用 IOC 三部曲 (一. 使用 ASP.NET Core 自带的 IOC 容器) ASP.NET Core 中使用 IOC 三部曲 (二. 采用 Autofac 来替换 IOC 容器, 并实现属性注入)
ASP.NET Core 中使用 IOC 三部曲 (三. 采用替换后的 Autofac 来实现 AOP 拦截)
正文
上一篇我们说过 ASP.NET Core 中自带的 IOC 容器是属于轻量级的, 功能并不是很多, 只是提供了基础功能而已..
所以今天我们主要讲讲如何采用 Autofac 来替换 IOC 容器, 并实现属性注入
注意: 本文需要读者理解 DI IOC 并使用过相关框架.
1. 将默认的 IOC 容器替换为 Autofac
首先, 我们需要从 nuget 引用相关的包.
Autofac
Autofac.Extensions.DependencyInjection(这个包扩展了一些微软提供服务的类. 来方便替换 autofac)
然后, 我们修改 Startup 中的 ConfigureServices 代码如下:
这里我们使用了 AutoFac 的功能之一, 模块化注入. 也就是 RegisterModule 这里, DefaultModule 是我们的注入模块, 代码很简单, 如下:
public IServiceProvider ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddDbContext < BloggingContext > ();
services.AddDirectoryBrowser();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule < DefaultModule > ();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
解释一下, 在上面的代码中,我们配置
public class DefaultModule : Module
{
protected override void Load(ContainerBuilder builder)
{
//注入测试服务
builder.RegisterType<TestService>().As<ITestService>();
}
}
IServiceProvider
从 Autofac 容器中解析(设置一个有效的 Autofac 服务适配器).然后在整个框架中使用它来解析控制器的依赖关系,并在
HttpContext
上公开所有其他用例的服务定位.
这样我们就完成了初步的 Autofac 容器替换. 下面我们创建控制器来看看效果. 代码如下:
当框架(通过一个命名为 DefaultControllerActivator 的服务)要创建一个控制器的实例时,它会解析 IServiceProvider 的所有构造函数依赖项. 在上面的代码中,它会使用 Autofac 容器来解析产生类.
public class AutoDIController : Controller
{
private readonly ITestService _testService;
public AutoDIController(ITestService testService)
{
_testService = testService;
}
// GET: AutoDI
public ActionResult Index()
{
ViewBag.date = _testService.GetList("Name");
return View();
}
}
这样就能初步的达到我们替换 IOC 容器的的效果了.. 但是,这个操作过程与 asp.net MVC 的不同之处在于. 控制器本身不会从容器中解析出来,所以服务只能从它的构造器参数中解析出来.所以. 这个过程, 让我们无法使用 Autofac 的一些更高级功能. 比如属性注入 (关于属性注入的好坏.. 属于仁者见仁智者见智的东西, 这里我们不讨论它是好还是坏.)
2. 如何使用 Autofac 的高级功能, 属性注入.
我们回到 Autofac 设置代码,并设置属性注入如下:
注入模块的代码修改如下:
var containerBuilder = new ContainerBuilder();
//模块化注入
containerBuilder.RegisterModule < DefaultModule > ();
//属性注入控制器
containerBuilder.RegisterType < AutoDIController > ().PropertiesAutowired();
containerBuilder.Populate(services);
然后修改我们的控制器代码如下:
//属性注入
builder.RegisterType < TestService > ().As < ITestService > ().PropertiesAutowired();
这里我们剔除了控制器的构造函数.
public class AutoDIController : BaseController
{
public ITestService _testService { get; set; }
// GET: AutoDI
public ActionResult Index()
{
ViewBag.date = _testService.GetList("Name");
return View();
}
}
我们运行代码, 会发现_testService 为 null, 所以根本没有注入成功. 失败的原因上面我们已经解释过了... 但是还是强调一下吧.. 虽然控制器的构造函数依赖性将由 MVC 从 IServiceProvider 解决(也就是我们之前构造函数注入的例子),但是控制器本身的实例(以及它的处理)却是由框架创建和拥有的,而不是由容器所有.
那么我们该如何改变控制器本身的创建和所有者呢
我们会在 Microsoft.Extensions.DependencyInjection 中找到一个方法. 叫做 AddControllersAsServices
它的注释翻译过来为: 将控制器的寄宿器转为注册的服务 (也就是我们替换的 autofac).
但是, 注意.. 这里虽然是将控制的所有者改成了 autofac, 但是我们还是不能使用相关的属性注入方法. 所以, 我们到 GITHUB 上来看看这个方法源码如下.(这就是开源的好处...):
我们会发现最后一句..
public static IMvcBuilder AddControllersAsServices(this IMvcBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
var feature = new ControllerFeature();
builder.PartManager.PopulateFeature(feature);
foreach (var controller in feature.Controllers.Select(c => c.AsType()))
{
builder.Services.TryAddTransient(controller, controller);
}
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
return builder;
}
builder.Services.Replace(ServiceDescriptor.Transient < IControllerActivator, ServiceBasedControllerActivator > ());
意思是用 ServiceBasedControllerActivator 替换
DefaultControllerActivator
(意味着框架现在会尝试从 IServiceProvider 中解析控制器实例)
.. 这下终于真相大白了..
我们只需要修改配置服务的代码如下:
注意, 替换的方法一定要在 addMVC 之前..
public IServiceProvider ConfigureServices(IServiceCollection services) {
//替换控制器所有者
services.Replace(ServiceDescriptor.Transient < IControllerActivator, ServiceBasedControllerActivator > ());
services.AddMvc();
services.AddDbContext < BloggingContext > ();
services.AddDirectoryBrowser();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule < DefaultModule > ();
//采用属性注入控制器
containerBuilder.RegisterType < AutoDIController > ().PropertiesAutowired();
// containerBuilder.RegisterTypes(feature.Controllers.Select(ti => ti.AsType()).ToArray()).PropertiesAutowired();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
然后我们运行我们的控制器代码. 效果如图:
如图所示,_testService 已经被实例化了. 说明我们的属性注入就成功了~
写在最后
本篇到此就结束了, 下篇我们讲解, 如何使用 Autofac 的高级功能来实现我们的切面编程 (AOP)
喜欢的请点个推荐和关注,~ 有问题也希望各位批评指正~.
来源: https://www.cnblogs.com/GuZhenYin/p/8301500.html