在以前的文章【ASP.NET Core 整合Autofac和Castle實現自動AOP攔截】中,咱們講過除了ASP.NETCore自帶的IOC容器外,如何使用Autofac來接管IServiceProvider進行依賴注入。html
最近老有想法在ASP.NET Mvc Core中實現Controller的屬性值的依賴注入,可是找遍了Microsoft.Extensions.DependencyInjection類庫也沒找到對應的方法,並且查看源代碼以後發現其都是針對構造器進行依賴注入的,並無對屬性或字段進行依賴注入。git
官方給咱們的兩種獲取依賴注入結果的方法:ActivatorUtilities.CreateInstance和IServiceProvider.GetService,這兩個方法的區別,這裏我就不詳細闡述了,有興趣的朋友能夠本身去查看一下這兩個類的源代碼:ServiceProvider和ActivatorUtilities,但總得來講兩個方法在建立對象時都沒有注入屬性值。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的身影,它的擴展能力無需解釋。學習
其使這個例子中使用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
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); } } } }
builder.RegisterTypes(feature.Controllers.Select(ti => ti.AsType()).ToArray()).PropertiesAutowired();
Autofac中經過對ControllerFeature中的Controller進行IOC註冊,並使用PropertiesAutowired開啓屬性注入。
這也是最重要的一步,經過查看源代碼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