zoukankan      html  css  js  c++  java
  • NET Core源代码通过Autofac实现依赖注入

    查看.NET Core源代码通过Autofac实现依赖注入到Controller属性

     

    一、前言

      在之前的文章【ASP.NET Core 整合Autofac和Castle实现自动AOP拦截】中,我们讲过除了ASP.NETCore自带的IOC容器外,如何使用Autofac来接管IServiceProvider进行依赖注入。

      最近老有想法在ASP.NET Mvc Core中实现Controller的属性值的依赖注入,但是找遍了Microsoft.Extensions.DependencyInjection类库也没找到对应的方法,而且查看源代码之后发现其都是针对构造器进行依赖注入的,并没有对属性或字段进行依赖注入。

      官方给我们的两种获取依赖注入结果的方法:ActivatorUtilities.CreateInstanceIServiceProvider.GetService,这两个方法的区别,这里我就不详细阐述了,有兴趣的朋友可以自己去查看一下这两个类的源代码:ServiceProviderActivatorUtilities,但总得来说两个方法在创建对象时都没有注入属性值。

      简单的调用这两个方法:首先在Startup.ConfigureServices函数中,添加语句services.AddTransient<IUser, MyUser>();

    1.   1. IUser user = ActivatorUtilities.CreateInstance(serviceProvider, typeof(IUser));
    2.   2. IUser user = serviceProvider.GetService(typeof(IUser))

      这两个函数的返回结果都是一样的,而且如果MyUser的构造器中有接口类型的话,两个方法也同样会进行依赖注入,但是都不会对创建出的对象属性进行注入。但是这两个方法还是有原理上的不同,ActivatorUtilities是通过构建ExpressionTree的方式对类型的构造器进行构造并创建出对象的,并使用IServiceProvider注入的构造器;而ServiceProvider则是完全通过依赖注入的生命周期的CallSite,对类型进行递归创建对象的。

      如果非要说那个方法更好的话,其实显而易见IServiceProvider是一个接口,而ActivatorUtilities是一组方法,而且ASP.NET Core中的DI生命周期中到处都是ServiceProvider的身影,它的扩展能力无需解释。

    二、使用Autofac

      其使这个例子中使用Autofac就是为了偷懒而已,主要是autofac已经支持属性的依赖注入了。但是确无法直接使用,通过研究ASP.NET Core MVC的源代码,我找到了解决方法,并借助Autofac来完成Controller属性的依赖注入操作。

      在上一篇介绍Autofac文章中提到过,Autofac是通过修改Startup.ConfigureServices函数的返回值,及返回值由void修改成IServiceProvider来完成的。

    1. public IServiceProvider ConfigureServices(IServiceCollection services)
    2. {
    3. var builder = new ContainerBuilder();
    4. services.AddMvc();
    5. builder.Populate(services);
    6. this.ApplicationContainer = builder.Build();
    7. return new AutofacServiceProvider(this.ApplicationContainer);
    8. }

      通过返回AutofacServiceProvider类型的IServiceProvider,Autofac就通过装饰模式就接管了ServiceProvider。但是只是接管IServiceProvider以后,我们会发现这并不能注入属性值,经过对ASP.NET Core源代码的研究,整理了如下思路:

      1.找到所有Controller的类型

    1. var manager = new ApplicationPartManager();
    2. manager.ApplicationParts.Add(new AssemblyPart(assembly));
    3. manager.FeatureProviders.Add(new ControllerFeatureProvider());
    4. var feature = new ControllerFeature();
    5. manager.PopulateFeature(feature);

       通过ApplicationPartManager,ASP.NET Core管理着所有程序组件,这里的AssemblyPart是一个程序集组件,也就是说ASP.NET Core MVC会在这个程序集中查找Controller类型或其它使用的类型。我们也可以通过这个方法来添加一个程序集,用于把MVC的项目拆成两个独立的项目,比如Controller项目和View项目等。

      ControllerFeatureProvider这个类看名字就知道它用于是查找Controller类型的。我们来摘一段它的代码看看:

    1. public void PopulateFeature(IEnumerable<ApplicationPart> parts,ControllerFeature feature)
    2. {
    3. foreach (var part in parts.OfType<IApplicationPartTypeProvider>())
    4. {
    5. foreach (var type in part.Types)
    6. {
    7. if (IsController(type) &&!feature.Controllers.Contains(type))
    8. {
    9. feature.Controllers.Add(type);
    10. }
    11. }
    12. }
    13. }

      2.通过Autofac对Controller类型进行注册

    1. builder.RegisterTypes(feature.Controllers.Select(ti => ti.AsType()).ToArray()).PropertiesAutowired();

      Autofac中通过对ControllerFeature中的Controller进行IOC注册,并使用PropertiesAutowired开启属性注入。

      3.修改默认的Controller创建者,使用Autofac的ServiceProvider完成Controller的创建工作。

      这也是最重要的一步,通过查看源代码ASP.NET Core默认使用DefaultControllerActivator类对Controller进行创建工作;但是找到这个类的Create函数发布它其实调用的是ActivatorUtilities来创建对象的。前面也说过这个的话,在创建类型对象时,IServiceProvdier只负责对构造器中的参数进行查找注入,创建对象的操作还是由ActivatorUtilities来create出来的,这样也就没用利用上autofac替换的ServiceProvider,也就是说ActivatorUtilities并没有扩展点来使用我们提供的方法进行替换,所以才造成了无法注入的问题。

      下面代码添加到Services.AddMvc();之前,如下:

    1. services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());

      其实就是用ServiceBasedControllerActivator来替换默认的DefaultControllerActivator ;来看看它的源代码吧,一下就明白了:

    1. public object Create(ControllerContext actionContext)
    2. {
    3. if (actionContext == null)
    4. {
    5. throw new ArgumentNullException(nameof(actionContext));
    6. }
    7. var controllerType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType();
    8. return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
    9. }

      这里的RequestServices就是IServiceProvider所以都懂的,这里使用的已经是咱们替换过用Provider了。

      最后再加一个Demo,看看属性User是不是被注入值了:

    1. public class HomeController : Controller
    2. {
    3. public IUserManager User { set; get; }
    4. public IActionResult Index()
    5. {
    6. User.Register("hello");
    7. return View();
    8. }
    9. }

    三、最后

      ASP.NET Core的源代码实在是学习的好材料,每一个组件都是一个扩展,每一个组件都有一组小部件;真正的组件式开发!  

      

      DEMO的Git地址:https://github.com/maxzhang1985/AutofacCastle.AspNetCore.Demo

      GitHub:https://github.com/maxzhang1985/YOYOFx  如果觉还可以请Star下, 欢迎一起交流。

      .NET Core 和 YOYOFx 的交流群: 214741894  

      

  • 相关阅读:
    【读书笔记】iOS-UDID
    【读书笔记】iOS-优化内存
    【读书笔记】iOS-处理内存警告
    【读书笔记】iOS-方法声明
    【读书笔记】iOS-属性
    【读书笔记】iOS-强类型与弱类型
    【读书笔记】iOS-Objective-C编程
    【读书笔记】iOS-Interface Builder
    【读书笔记】iOS-开发者证书
    【读书笔记】iOS-成为一名开发者
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/6078434.html
Copyright © 2011-2022 走看看