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

1、前言

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

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

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

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

  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的身影,它的扩展能力无需解释。学习

2、使用Autofac

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

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

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源代码的研究,整理了以下思路:spa

  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项目等。code

  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();
        }
}

 

 

3、最后

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

  

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

 

  GitHub:https://github.com/maxzhang1985/YOYOFx  若是觉还能够请Star下, 欢迎一块儿交流。

 

  .NET Core 和 YOYOFx 的交流群: 214741894  

相关文章
相关标签/搜索