如何将 Autofac 整合进 Net6.0 Core MVC 项目中

一、前言
    1、简而言之
        Asp.Net Core Mvc,我也用了很长一段时间了,它现在的编程模型和方式还是特别棒的,都是组件开发,什么都可以替换,当然了,您别抬杠,有些还是不能替换的。自从我们进入了跨平台开发的时代,IOC容器也成了一个不可或缺的东西了。微软为我们提供了一个默认实现,那就是 IServiceCollection,当时我们可以替换掉它,今天我就试试,替换一下,把我的一些经验也写出来,以防以后忘记,不知道去哪里找了。
        当然了,这个不是很难,也希望高手不要见笑,对于我来说,好记性不如烂笔头,好的东西我就记录下来,有使用的地方,可以直接来找。

    2、开发环境。
        我的开发环境没有发生变化,具体如下:
          操作系统:Windows10 Professional
          开发平台:Asp.Net Core Mvc 6.0
          开发语言:C#
          开发工具:Visual Studio 2022

二、操作步骤

    1、第一,我们当然要新建一个 Asp.Net Core MVC 的项目,项目都没有,其他的都是胡扯了,我项目的名称是:PatrickLiu.Autofac.MvcConcordance。
        如何将 Autofac 整合进 Net6.0 Core MVC 项目中

    2、我们为我们的项目增加相应的程序包。分别是:Autofac、Autofac.Extensions.DependencyInjection、Autofac.Extras.DynamicProxy,Castle.Core
        1】、Autofac 提供最基础、最核心的功能。
        2】、Autofac.Extensions.DependencyInjection 提供和 Asp.Net Core MVC 集成的接口。
        3】、Autofac.Extras.DynamicProxy 提供对AOP的支持,通过动态代理实现。
        4】、Castle.Core 实现针对 Core 版本的支持,也是支持 AOP 的必需组件。

         如何将 Autofac 整合进 Net6.0 Core MVC 项目中

    3、这部分是重点,在 Program 程序中配置。具体代码在里面,很简单,就不多说了。

 1 using Autofac;  2 using Autofac.Extensions.DependencyInjection;  3 using Autofac.Extras.DynamicProxy;  4 using Castle.DynamicProxy;  5 using Microsoft.AspNetCore.Mvc;  6 using Microsoft.AspNetCore.Mvc.Controllers;  7 using PatrickLiu.Autofac.Contracts;  8 using PatrickLiu.Autofac.Extensions;  9 using PatrickLiu.Autofac.Extensions.Aops; 10 using PatrickLiu.Autofac.Models; 11  12 var builder = WebApplication.CreateBuilder(args); 13 builder.Services.AddControllersWithViews(); 14  15 #region 整合 Autofac 16  17 builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); 18  19 builder.Host.ConfigureContainer<ContainerBuilder>(builder => 20 { 21     builder.RegisterType<ChiesePerson>().As<IPerson>(); 22     builder.RegisterType<SinglePerson>(); 23  24     #region 服务类型支持属性注入,红色表示是对属性注入的支持,哪个类型需要属性注入,在注册的时候使用 PropertiesAutowired()方法,里面参数是属性选择器。25  26     builder.RegisterType<PropertyPerson>().As<IPropertyPerson>().PropertiesAutowired(new CustomPropertySelector()); 27     builder.RegisterType<PropertyTwoPerson>().As<IPropertyTwoPerson>(); 28     builder.RegisterType<PropertyThreePerson>().As<IPropertyThreePerson>(); 29     builder.RegisterType<SinglePerson>(); 30  31     #endregion 32  33     #region AOP支持,红色标注的是关键实现。 34  35     builder.RegisterType<AOPPerson>().As<IAOPPerson>().EnableInterfaceInterceptors(); 36     builder.RegisterType<AOPClassPerson>().As<IAOPClassPerson>().EnableClassInterceptors(new ProxyGenerationOptions() 37     { 38         Selector = new CustomInterceptorSelector() 39     }); 40     builder.RegisterType<AOPCachePerson>().As<IAOPCachePerson>().EnableClassInterceptors(); 41  42     builder.RegisterType<CustomClassInterceptor>(); 43     builder.RegisterType<CustomInterfaceInterceptor>(); 44     builder.RegisterType<CustomCacheInterceptor>(); 45  46     #endregion 47  48     #region 单接口多实例 49  50     builder.RegisterType<MultiPerson>().Keyed<IMultiPerson>("MultiPerson"); 51     builder.RegisterType<MultiTwoPerson>().Keyed<IMultiPerson>("MultiTwoPerson"); 52     builder.RegisterType<MultiThreePerson>().Keyed<IMultiPerson>("MultiThreePerson"); 53  54     #endregion 55  56     #region 让控制器支持属性注入 57  58     var controllerBaseType = typeof(ControllerBase); 59     builder.RegisterAssemblyTypes(typeof(Program).Assembly) 60     .Where(t => controllerBaseType.IsAssignableFrom(t) && controllerBaseType != t) 61     .PropertiesAutowired(new CustomPropertySelector()); 62  63     builder.RegisterType<ServiceBasedControllerActivator>().As<IControllerActivator>(); 64  65     #endregion 66 }); 67  68 #region 支持 Autofac 属性注入,该方法可以使用,也可以不使用。作用是我们的控制器要使用 Autofac 容器来创建,替换原始的 Controller 激活器。 69  70 //builder.Services.AddTransient<IControllerActivator, ServiceBasedControllerActivator>(); 71 //builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); 72  73 #endregion 74  75 #endregion 76  77 var app = builder.Build(); 78  79 app.UseStaticFiles(); 80  81 app.UseRouting(); 82  83 app.MapControllerRoute("default", "{controller=AOP}/{action=index}/{id?}"); 84  85 app.Run();

    4、Autofac 支持属性注入,默认是所有属性的类型如果是注册的服务类型,就会全部赋值,但是,我们也可以实现 IPropertySelector 接口,自定义哪个属性需要注入。

 1 using Autofac.Core;  2 using System.Reflection;  3   4 namespace PatrickLiu.Autofac.Extensions  5 {  6     public sealed class CustomPropertySelector : IPropertySelector  7     {  8         public bool InjectProperty(PropertyInfo propertyInfo, object instance)  9         { 10             return propertyInfo.IsDefined(typeof(CustomPropertySelectorAttribute), false); 11         } 12     } 13 }

      有了选择器,我们也需要定义特性(Attribute),标注属性(Property)就可以。

 1 namespace PatrickLiu.Autofac.Extensions  2 {  3     /// <summary>  4     ///   5     /// </summary>  6     [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]  7     public sealed class CustomPropertySelectorAttribute : Attribute  8     {  9     } 10 }

       有了这两个,我们在把我们自定义的属性选择器 CustomPropertySelector 作为参数,传递给 PropertiesAutowired(new CustomPropertySelector())方法,就完成操作了。
      

 1 using Microsoft.AspNetCore.Mvc;  2 using PatrickLiu.Autofac.Contracts;  3 using PatrickLiu.Autofac.Extensions;  4 using PatrickLiu.Autofac.Models;  5   6 namespace PatrickLiu.Autofac.MvcConcordance.Controllers  7 {  8     /// <summary>  9     ///  10     /// </summary> 11     public class PropertyInjectionController : Controller 12     { 13         private readonly IPropertyPerson _propertyPerson; 14  15         /// <summary> 16         ///  17         /// </summary> 18         /// <param name="propertyPerson"></param> 19         public PropertyInjectionController(IPropertyPerson propertyPerson) 20         { 21             _propertyPerson = propertyPerson; 22         } 23  24         /// <summary> 25         /// 这里就是控制器的属性,需要自动初始化。 26         /// </summary> 27         [CustomPropertySelector] 28         public SinglePerson? SinglePerson { get; set; } 29  30         /// <summary> 31         ///  32         /// </summary> 33         /// <returns></returns> 34         public IActionResult Index() 35         { 36             _propertyPerson.Process(); 37  38             return View(); 39         } 40     } 41 }

    5、Autofac 支持两种类型 AOP,分别是:EnableClassInterceptors 和 EnableInterfaceInterceptors ,类拦截器必须使用 [Intercept(typeof(CustomClassInterceptor))]标注需要实现 AOP 的实现类上,如果是接口拦截器,就必须将 [Intercept(typeof(CustomInterfaceInterceptor))] 标注在需要实现 AOP 的接口类型上。
        1】、EnableClassInterceptors:类拦截器,它的方法必须是 virtual 虚方法,才可以支持 AOP。
        2】、EnableInterfaceInterceptors:接口拦截器,没有相关限制,该接口的实现类的方法都会实现 AOP。
        这就是接口拦截器。

 1 using Autofac.Extras.DynamicProxy;  2 using PatrickLiu.Autofac.Extensions.Aops;  3   4 namespace PatrickLiu.Autofac.Contracts  5 {  6     /// <summary>  7     ///   8     /// </summary>  9     [Intercept(typeof(CustomInterfaceInterceptor))] 10     public interface IAOPPerson 11     { 12         /// <summary> 13         ///  14         /// </summary> 15         void Process(); 16  17         /// <summary> 18         ///  19         /// </summary> 20         void ProcessTwo(); 21  22         /// <summary> 23         ///  24         /// </summary> 25         void ProcessThree(); 26     } 27 }

        以下是类拦截器。

 1 using Autofac.Extras.DynamicProxy;  2 using PatrickLiu.Autofac.Contracts;  3 using PatrickLiu.Autofac.Extensions.Aops;  4   5 namespace PatrickLiu.Autofac.Models  6 {  7     /// <summary>  8     ///   9     /// </summary> 10     [Intercept(typeof(CustomClassInterceptor))] 11     public class AOPClassPerson : IAOPClassPerson 12     { 13         /// <summary> 14         ///  15         /// </summary> 16         public virtual void ProcessAOP() 17         { 18             Console.WriteLine("AOPClassPerson.ProcessAOP()"); 19         } 20  21         /// <summary> 22         ///  23         /// </summary> 24         public void Process() 25         { 26             Console.WriteLine("AOPClassPerson.Process()"); 27         } 28     } 29 }

    6、我们要自定义我们的拦截器,然后再标注的类型标注 [Intercept(typeof(自定义拦截器))],就可以使用,我分别定义了两个拦截器,一个用于类,一个用于接口,其实没有本质区别,实现的接口都是一样的,该接口就是:IInterceptor。

 1 using Castle.DynamicProxy;  2   3 namespace PatrickLiu.Autofac.Extensions.Aops  4 {  5     /// <summary>  6     /// 类级别的拦截器,标注在要实现AOP功能的类型上。  7     /// </summary>  8     public sealed class CustomClassInterceptor : IInterceptor  9     { 10         /// <summary> 11         ///  12         /// </summary> 13         /// <param name="invocation"></param> 14         public void Intercept(IInvocation invocation) 15         { 16             { 17                 Console.WriteLine("CustomClassInterceptor.Before"); 18             } 19             invocation.Proceed(); 20             { 21                 Console.WriteLine("CustomClassInterceptor.After"); 22             } 23         } 24     } 25 }

 1 using Castle.DynamicProxy;  2   3 namespace PatrickLiu.Autofac.Extensions.Aops  4 {  5     /// <summary>  6     /// 接口级别的拦截器,标注在要实现AOP功能的接口类型上。  7     /// </summary>  8     public sealed class CustomInterfaceInterceptor : IInterceptor  9     { 10         /// <summary> 11         ///  12         /// </summary> 13         /// <param name="invocation"></param> 14         public void Intercept(IInvocation invocation) 15         { 16             { 17                 Console.WriteLine("CustomInterfaceInterceptor.Before"); 18             } 19             invocation.Proceed(); 20             { 21                 Console.WriteLine("CustomInterfaceInterceptor.After"); 22             } 23         } 24     } 25 }

    7、当然,我们也可以不标注 [Intercept(typeof(自定义拦截器))],我们可以实现 IInterceptorSelector 接口,实现自定义使用哪些拦截器。代码如下:
       

 1 using Castle.DynamicProxy;  2 using System.Reflection;  3   4 namespace PatrickLiu.Autofac.Extensions.Aops  5 {  6     /// <summary>  7     ///   8     /// </summary>  9     public class CustomInterceptorSelector : IInterceptorSelector 10     { 11         /// <summary> 12         ///  13         /// </summary> 14         /// <param name="type"></param> 15         /// <param name="method"></param> 16         /// <param name="interceptors">如果类型有标注拦截器,这里会获取所有拦截器。</param> 17         /// <returns></returns>        18         public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors) 19         { 20             IList<IInterceptor> interceptorsList = new List<IInterceptor>(); 21             interceptorsList.Add(new CustomInterfaceInterceptor()); 22        在这个方法里面,我们可以过滤拦截器,想是哪个起作用哪个就起作用。返回的拦截器,就是起作用的拦截器。 23             return interceptorsList.ToArray(); 24         } 25     } 26 }

       我们可以选择拦截器,也需要在Program 里体现,builder.RegisterType<AOPClassPerson>().As<IAOPClassPerson>().EnableClassInterceptors(new ProxyGenerationOptions()  {     Selector = new CustomInterceptorSelector()   });

    8、Autofac 默认支持构造函数注入,如果有多个构造函数,如果构造函数的参数都是需要注入的服务类型,默认选择依赖注册服务参数最多的构造函数会被执行。当然我们也可以选择指定的构造函数来初始化类型实例,该方法就是 .UsingConstructor(typeof(参数类型))。
      

builder.RegisterType<ChiesePerson>().As<IPerson>().UsingConstructor(typeof(int));

 1 using Autofac;  2 using Microsoft.AspNetCore.Mvc;  3 using PatrickLiu.Autofac.Contracts;  4 using PatrickLiu.Autofac.Models;  5   6 namespace PatrickLiu.Autofac.MvcConcordance.Controllers  7 {  8     /// <summary>  9     ///  10     /// </summary> 11     public class HomeController : Controller 12     { 13         private readonly IPerson _person; 14         private readonly IServiceProvider _serviceProvider; 15         private readonly IComponentContext _componentContext; 16  17         /// <summary> 18         ///  19         /// </summary> 20         /// <param name="person"></param> 21         /// <param name="serviceProvider">服务提供器,类型是 AutofacServiceProvider,获取类型。</param> 22         /// <param name="componentContext">autofac 的上下文对象,可以获取容器中的服务。</param> 23         public HomeController(IPerson person, IServiceProvider serviceProvider, IComponentContext componentContext) 24         { 25             _person = person; 26             _serviceProvider = serviceProvider; 27             _componentContext = componentContext; 28         } 29  30         public IActionResult Index() 31         { 32             _person.Eat("炸酱面"); 33  34             var single=_serviceProvider.GetService<SinglePerson>(); 35             single!.Eat("残羹冷炙"); 36  37             var singleTwo=_componentContext.Resolve<SinglePerson>(); 38             singleTwo.Eat("残羹剩饭"); 39  40             return View(); 41         } 42     } 43 }

三、结束语
    平台本身提供了自己的容器实现,当然,我们也可以替换它,使用其他的 IOC容器。不学不知道,一学吓一跳,东西还有很多不知道了。平凡的我,只能继续努力,苍天不负有心人,努力就会有回报。不忘初心,继续努力吧。
    

发表评论

相关文章