• 如何将 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。
            


        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 的必需组件。

            

        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(builder =>
    20 {
    21     builder.RegisterType().As();
    22     builder.RegisterType();
    23 
    24     #region 服务类型支持属性注入,红色表示是对属性注入的支持,哪个类型需要属性注入,在注册的时候使用 PropertiesAutowired()方法,里面参数是属性选择器。25 
    26     builder.RegisterType().As().PropertiesAutowired(new CustomPropertySelector());
    27     builder.RegisterType().As();
    28     builder.RegisterType().As();
    29     builder.RegisterType();
    30 
    31     #endregion
    32 
    33     #region AOP支持,红色标注的是关键实现。
    34 
    35     builder.RegisterType().As().EnableInterfaceInterceptors();
    36     builder.RegisterType().As().EnableClassInterceptors(new ProxyGenerationOptions()
    37     {
    38         Selector = new CustomInterceptorSelector()
    39     });
    40     builder.RegisterType().As().EnableClassInterceptors();
    41 
    42     builder.RegisterType();
    43     builder.RegisterType();
    44     builder.RegisterType();
    45 
    46     #endregion
    47 
    48     #region 单接口多实例
    49 
    50     builder.RegisterType().Keyed("MultiPerson");
    51     builder.RegisterType().Keyed("MultiTwoPerson");
    52     builder.RegisterType().Keyed("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().As();
    64 
    65     #endregion
    66 });
    67 
    68 #region 支持 Autofac 属性注入,该方法可以使用,也可以不使用。作用是我们的控制器要使用 Autofac 容器来创建,替换原始的 Controller 激活器。
    69 
    70 //builder.Services.AddTransient();
    71 //builder.Services.Replace(ServiceDescriptor.Transient());
    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     /// 
     4     /// 
     5     /// 
     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     /// 
     9     /// 
    10     /// 
    11     public class PropertyInjectionController : Controller
    12     {
    13         private readonly IPropertyPerson _propertyPerson;
    14 
    15         /// 
    16         /// 
    17         /// 
    18         /// 
    19         public PropertyInjectionController(IPropertyPerson propertyPerson)
    20         {
    21             _propertyPerson = propertyPerson;
    22         }
    23 
    24         /// 
    25         /// 这里就是控制器的属性,需要自动初始化。
    26         /// 
    27         [CustomPropertySelector]
    28         public SinglePerson? SinglePerson { get; set; }
    29 
    30         /// 
    31         /// 
    32         /// 
    33         /// 
    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     /// 
     7     /// 
     8     /// 
     9     [Intercept(typeof(CustomInterfaceInterceptor))]
    10     public interface IAOPPerson
    11     {
    12         /// 
    13         /// 
    14         /// 
    15         void Process();
    16 
    17         /// 
    18         /// 
    19         /// 
    20         void ProcessTwo();
    21 
    22         /// 
    23         /// 
    24         /// 
    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     /// 
     8     /// 
     9     /// 
    10     [Intercept(typeof(CustomClassInterceptor))]
    11     public class AOPClassPerson : IAOPClassPerson
    12     {
    13         /// 
    14         /// 
    15         /// 
    16         public virtual void ProcessAOP()
    17         {
    18             Console.WriteLine("AOPClassPerson.ProcessAOP()");
    19         }
    20 
    21         /// 
    22         /// 
    23         /// 
    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     /// 
     6     /// 类级别的拦截器,标注在要实现AOP功能的类型上。
     7     /// 
     8     public sealed class CustomClassInterceptor : IInterceptor
     9     {
    10         /// 
    11         /// 
    12         /// 
    13         /// 
    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     /// 
     6     /// 接口级别的拦截器,标注在要实现AOP功能的接口类型上。
     7     /// 
     8     public sealed class CustomInterfaceInterceptor : IInterceptor
     9     {
    10         /// 
    11         /// 
    12         /// 
    13         /// 
    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     /// 
     7     /// 
     8     /// 
     9     public class CustomInterceptorSelector : IInterceptorSelector
    10     {
    11         /// 
    12         /// 
    13         /// 
    14         /// 
    15         /// 
    16         /// 如果类型有标注拦截器,这里会获取所有拦截器。
    17         ///        
    18         public IInterceptor[] SelectInterceptors(Type type, MethodInfo method, IInterceptor[] interceptors)
    19         {
    20             IList interceptorsList = new List();
    21             interceptorsList.Add(new CustomInterfaceInterceptor());
    22        在这个方法里面,我们可以过滤拦截器,想是哪个起作用哪个就起作用。返回的拦截器,就是起作用的拦截器。
    23             return interceptorsList.ToArray();
    24         }
    25     }
    26 }
    复制代码

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

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

    builder.RegisterType().As().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     /// 
     9     /// 
    10     /// 
    11     public class HomeController : Controller
    12     {
    13         private readonly IPerson _person;
    14         private readonly IServiceProvider _serviceProvider;
    15         private readonly IComponentContext _componentContext;
    16 
    17         /// 
    18         /// 
    19         /// 
    20         /// 
    21         /// 服务提供器,类型是 AutofacServiceProvider,获取类型。
    22         /// autofac 的上下文对象,可以获取容器中的服务。
    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();
    35             single!.Eat("残羹冷炙");
    36 
    37             var singleTwo=_componentContext.Resolve();
    38             singleTwo.Eat("残羹剩饭");
    39 
    40             return View();
    41         }
    42     }
    43 }
    复制代码


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

  • 相关阅读:
    Nacos在企业生产中如何使用集群环境?
    [element-ui] el-dialog和v-viewer的层级问题
    【Reinforcement Learning】Ubuntu中mujoco210 mujoco_py D4RL安装及错误解决
    Fiddler工具讲解
    C++ 中的虚函数和多态性
    C++基础知识(二十五)--- STL案例之演讲比赛
    04 在MSYS2中安装QEMU
    Wasserstein Slim GAIN with Gradient Penalty(WSGAIN-GP)介绍及代码实现——基于生成对抗网络的缺失数据填补
    Windows静态库用C++代码调用C语言的旧库方法extern ”c“
    GBase 8c 创建和管理表(二)
  • 原文地址:https://www.cnblogs.com/PatrickLiu/p/17147826.html