3

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

 1 year ago
source link: https://www.cnblogs.com/PatrickLiu/p/17147826.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

posts - 184,  comments - 434,  views -

121万

一、前言
    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。

1048776-20230223141513203-785172032.png

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

1048776-20230223142131893-1493839549.png

    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容器。不学不知道,一学吓一跳,东西还有很多不知道了。平凡的我,只能继续努力,苍天不负有心人,努力就会有回报。不忘初心,继续努力吧。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK