查看.NET Core源代碼通過Autofac實現依賴注入到Controller屬性
一、前言
在之前的文章【ASP.NET Core 整合Autofac和Castle實現自動AOP攔截】中,我們講過除了ASP.NETCore自帶的IOC容器外,如何使用Autofac來接管IServiceProvider進行依賴注入。
最近老有想法在ASP.NET Mvc Core中實現Controller的屬性值的依賴注入,但是找遍了Microsoft.Extensions.DependencyInjection類庫也沒找到對應的方法,而且查看源代碼之后發現其都是針對構造器進行依賴注入的,並沒有對屬性或字段進行依賴注入。
官方給我們的兩種獲取依賴注入結果的方法:ActivatorUtilities.CreateInstance和IServiceProvider.GetService,這兩個方法的區別,這里我就不詳細闡述了,有興趣的朋友可以自己去查看一下這兩個類的源代碼:ServiceProvider和ActivatorUtilities,但總得來說兩個方法在創建對象時都沒有注入屬性值。
簡單的調用這兩個方法:首先在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