一、为啥要使用第3方依赖注入框架 ?
1、微软官方提供的依赖注入框架已经很强大,一般情况无需第3方DI容器;
2、但是在一些特殊情况,需要第3方DI容器增强依赖注入功能,一般有如下几种情况:
(1) 基于名称的注入,基于Key的注入
比如一个解决方案(一个webapi项目)同时作为多个MQ的消费者
(2) 属性注入
(3) 子容器
(4) 基于动态代理的 AOP
(5)整个应用程序级的生命周期;
二、普通的使用方法:
1、Nuget添加组件:Autofac.Extensions.DependencyInjection
2、Program里添加AutoFac:
UseServiceProviderFactory(new AutofacServiceProviderFactory())
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApplication1
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
3、StartUp里添加一个方法
public void ConfigureContainer(ContainerBuilder builder)
4、与微软自带的几个常用的用法:
public void ConfigureContainer(ContainerBuilder builder)
{
#region //1简单的使用--- 与默认DI容器相同的功能
//生命周期:默认,也就是瞬时模式==AddTransient
// services.AddScoped<IStudent, Student>(); --这个是微软自带的写法
builder.RegisterType<Student>().As<IStudent>();
//生命周期:默认,也就是瞬时模式==AddTransient
builder.RegisterType<Student>().As<IStudent>().InstancePerDependency();
//生命周期:Scope模式==AddScoped
builder.RegisterType<Student>().As<IStudent>().InstancePerLifetimeScope();
//生命周期:单例,AddSingleton
builder.RegisterType<Student>().As<IStudent>().SingleInstance();
#endregion
}
注意,写法还有有差别的,RegisterType的是类,As的是接口。
builder.RegisterType<Student>().As<IStudent>();
获取实例的方法与微软自带的容器获取实例方法一致。
比如在构造函数或者方法FromService里获取:
private readonly ILogger<HomeController> _logger;
private readonly IHostEnvironment _hostEnvironment;
private readonly IServiceProvider _services;
public HomeController(ILogger<HomeController> logger, IHostEnvironment hostEnvironment, IServiceProvider services)
{
_logger = logger;
_hostEnvironment = hostEnvironment;
_services = services;
}
public IActionResult Index3([FromServices] IEnumerable<IStudent> services)
{
Console.WriteLine("开始。。。。");
foreach (var s in services)
{
Console.WriteLine($"获取到的实例为:{s.ToString()}:{s.GetHashCode()}");
}
return View();
}
public IActionResult Index2([FromServices] IHostEnvironment hostEnvironment, [FromServices] IStudent stu)
{
//1、通过构造函数注入,获取值
string path = _hostEnvironment.ContentRootPath;
//2、通过方法的FromServices注入进来,获取值
string path2 = hostEnvironment.ContentRootPath;
//通过GetRequiredService来检索服务
var hostEnvironment2 = HttpContext.RequestServices.GetRequiredService<IHostEnvironment>();
string path3 = hostEnvironment2.ContentRootPath;
string name = stu.Change();
int hashcode = stu.GetHashCode();
return View();
}
三、一些特别的用法:
1、基于名称的注入
在StartUp的 ConfigureContainer(ContainerBuilder builder)里注入:
//基于名称的注入
builder.RegisterType<Student>().Named<IStudent>("muxue").SingleInstance();
获取实例的方法,有2种方法:
(1) IServiceProvider里获取AutoFac根容器再进行获取实例;
(2)使用AutoFac自带的ILifetimeScope获取实例;
代码如下:
private readonly ILogger<HomeController> _logger;
private readonly IHostEnvironment _hostEnvironment;
private readonly IServiceProvider _services;
public HomeController(ILogger<HomeController> logger, IHostEnvironment hostEnvironment, IServiceProvider services)
{
_logger = logger;
_hostEnvironment = hostEnvironment;
_services = services;
}
/// <summary>
/// 基于名称的注入-的获取(沐雪原创)
/// </summary>
/// <param name="service"></param>
/// <returns></returns>
public IActionResult Index([FromServices] ILifetimeScope service)
{
Console.WriteLine("开始取对象。。。。");
//使用IServiceProvider 的方法
// var service= _services.GetAutofacRoot();
var stu = service.ResolveNamed<IStudent>("myName");
Console.WriteLine($"获取到的实例为:{stu.ToString()}:{stu.GetHashCode()}");
return View();
}
2、基于Key的注入:跟基于名称的注入类似,我们在使用RabbitMQ,一个项目有多个消费者的情况使用的demo
builder.Register((c, p) => RabbitHutch.CreateBus(c.Resolve<IConfiguration>().GetConnectionString("MQ1")))
.Keyed<IBus>("MQ1")
.SingleInstance();
builder.Register((c, p) => RabbitHutch.CreateBus(c.Resolve<IConfiguration>().GetConnectionString("MQ2")))
.Keyed<IBus>("MQ2")
.SingleInstance();
获取实例的方法:
var bus = _services.GetAutofacRoot().ResolveKeyed<IBus>("MQ1");
3、基于属性的注入(这个在微软自带的IOC好像也有类似的写法,比如,类里需要一个Configuration的一个节点值,可以先注册该节点值,然后就可以在类里直接使用了。)
也就是一个类A里有一个属性类B,代码如下:
public class StudentService: IStudentService
{
public AddressService addrService { get; set; }
public void ShowCode()
{
Console.WriteLine($"StudentService.ShowCode:{GetHashCode()},addrService 是否为空:{addrService == null}");
}
}
public class AddressService
{
}
addrService是StudentService的一个属性值;
注册的时候,需要先将addrService注册,在使用属性注册的方式注册StudentService;
#region 属性注册
builder.RegisterType<AddressService>();
builder.RegisterType<StudentService>().As<IStudentService>().PropertiesAutowired();
#endregion
四、AutoFac的切面编程--拦截器AOP的使用
第一眼看到拦截器AOP功能,我还以为是个鸡肋,.NetCore都已经有了那么强大的Filter作为AOP拦截器,为啥AutoFac还有这个玩意?
后来才发现,是自己想错了。AutoFac的拦截器,拦截的是注册在容器里的服务的实例里的方法。有点绕,简单说,他拦截的是方法,是一个实例的方法,是依赖注入注册在容器里的服务对应的实例。 而Filter则是对控制器Controller/Action的AOP的实现。两者拦截的对象不同。
AutoFac的拦截器是基于大名鼎鼎的Castle.Core来完成的。也就是Castle.Core里的一个动态代理DynamicProxy。
我们现在看下代码的实现!
1、Nuget新增加一个组件
Autofac.Extras.DynamicProxy
2、搞一个自定义的拦截器
using Castle.DynamicProxy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebApplication1
{
/// <summary>
/// 沐雪-自定义一个拦截器
/// </summary>
public class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("----- 拦截器开始啦 -------");
//方法的真正执行
invocation.Proceed();
Console.WriteLine("----- 拦截器结束啦 -------\r\n");
}
}
}
该拦截器继承自IInterceptor接口,我们要显示实现该接口的方法Intercept,注意里面的一句代码
invocation.Proceed();
这是调用真实方法。
3、开始注册了,将我们自定义的拦截器,以及要拦截的服务(接口和对象)都注册进来。
在ConfigureContainer 方法里写:
public void ConfigureContainer(ContainerBuilder builder)
{
//拦截器
builder.RegisterType<MyInterceptor>();
//IStudent需要这个拦截器
builder.RegisterType<Student>().As<IStudent>().InterceptedBy(typeof(MyInterceptor)).EnableInterfaceInterceptors();
}
我们再看下IStudent里的部分代码(demo代码):
public interface IStudent
{
string Change();
string Change2();
}
public class Student : IStudent
{
public string name { get; set; } = "小王";
public Student()
{
}
public Student(string initName)
{
name = initName;
}
public string Change()
{
Console.WriteLine("在change方法里");
name += "002";
return name;
}
public string Change2()
{
Console.WriteLine("在change2方法里");
name += "007";
return name;
}
}
4、最后一步,获取该实例,并且执行该实例的相应方法:
/// <summary>
/// 测试AutoFac拦截器
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public IActionResult Index([FromServices] IStudent services)
{
Console.WriteLine("\r\n沐雪大神爱吃鱼--Start。。。。\r\n");
Console.WriteLine($"获取到的实例为:{services.ToString()}:{services.GetHashCode()}\r\n");
services.Change();
Console.WriteLine("沐雪大神爱吃鱼--已经吃了一条。。。。\r\n");
services.Change2();
Console.WriteLine("沐雪大神爱吃鱼--吃完啦。。。。");
return View();
}
直接结果如下图: