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  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM