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
第一眼看到拦截器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(); }
直接结果如下图: