查看.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  

相關文章
相關標籤/搜索