zoukankan      html  css  js  c++  java
  • 查看.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. IUser user = ActivatorUtilities.CreateInstance(serviceProvider, typeof(IUser));
      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来完成的。

    复制代码
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
         var builder = new ContainerBuilder();
         services.AddMvc();
         builder.Populate(services);
         this.ApplicationContainer = builder.Build();
         return new AutofacServiceProvider(this.ApplicationContainer);
    }
    复制代码

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

      1.找到所有Controller的类型

    var manager = new ApplicationPartManager();
    manager.ApplicationParts.Add(new AssemblyPart(assembly));
    manager.FeatureProviders.Add(new ControllerFeatureProvider());
    var feature = new ControllerFeature();
    manager.PopulateFeature(feature);

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

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

    复制代码
    public void PopulateFeature(IEnumerable<ApplicationPart> parts,ControllerFeature feature)
    {
         foreach (var part in parts.OfType<IApplicationPartTypeProvider>())
         {
              foreach (var type in part.Types)
              {
                  if (IsController(type) &&!feature.Controllers.Contains(type))
                  {
                      feature.Controllers.Add(type);
                  }
              }
          }
    }
    复制代码

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

     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();之前,如下:

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

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

    复制代码
     public object Create(ControllerContext actionContext)
     {
          if (actionContext == null)
          {
              throw new ArgumentNullException(nameof(actionContext));
          }
    
          var controllerType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType();
          return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);
    }
    复制代码

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

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

    复制代码
     public class HomeController : Controller
    {
            public IUserManager User { set; get; }
    
            public IActionResult Index()
            {
                User.Register("hello");
                return View();
            }
    }
    复制代码

    三、最后

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

      

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

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

      .NET Core 和 YOYOFx 的交流群: 214741894  

      

     
     
    12
    0
     
     
     
    « 上一篇: 细说ASP.NET Core静态文件的缓存方式
    » 下一篇: ASP.NET Core中的ActionFilter与DI
  • 相关阅读:
    119. Pascal's Triangle II
    118. Pascal's Triangle
    112. Path Sum
    111. Minimum Depth of Binary Tree
    110. Balanced Binary Tree
    108. Convert Sorted Array to Binary Search Tree
    88. Merge Sorted Array
    83. Remove Duplicates from Sorted List
    70. Climbing Stairs
    陌陌面试经历
  • 原文地址:https://www.cnblogs.com/webenh/p/11605619.html
Copyright © 2011-2022 走看看