ASP.NET Core中使用IOC三部曲(二.採用Autofac來替換IOC容器,並實現屬性注入)
https://www.cnblogs.com/GuZhenYin/p/8301500.html
上一篇我們說過ASP.NET Core中自帶的IOC容器是屬於輕量級的,功能並不是很多,只是提供了基礎功能而已..
所以今天我們主要講講如何採用Autofac來替換IOC容器,並實現屬性注入
注意:本文需要讀者理解DI IOC並使用過相關框架.
1.將預設的IOC容器替換為Autofac
首先,我們需要從nuget引用相關的包.
Autofac
Autofac.Extensions.DependencyInjection(這個包擴充套件了一些微軟提供服務的類.來方便替換autofac)
然後,我們修改Startup中的ConfigureServices程式碼如下:
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddDbContext<BloggingContext>(); services.AddDirectoryBrowser(); var containerBuilder = new ContainerBuilder(); containerBuilder.RegisterModule<DefaultModule>(); containerBuilder.Populate(services); var container = containerBuilder.Build(); return new AutofacServiceProvider(container); }
這裡我們使用了AutoFac的功能之一,模組化注入.也就是RegisterModule 這裡, DefaultModule是我們的注入模組,程式碼很簡單,如下:
public class DefaultModule : Module { protected override void Load(ContainerBuilder builder) { //注入測試服務 builder.RegisterType<TestService>().As<ITestService>(); } }
解釋一下,在上面的程式碼中,我們配置IServiceProvider從Autofac容器中解析(設定一個有效的Autofac服務介面卡)。
然後在整個框架中使用它來解析控制器的依賴關係,並在HttpContext上公開所有其他用例的服務定位。
這樣我們就完成了初步的Autofac容器替換.下面我們建立控制器來看看效果.程式碼如下:
public class AutoDIController : Controller { private readonly ITestService _testService; public AutoDIController(ITestService testService) { _testService = testService; } // GET: AutoDI public ActionResult Index() { ViewBag.date = _testService.GetList("Name"); return View(); } }
當框架(通過一個命名為DefaultControllerActivator的服務)要建立一個控制器的例項時,它會解析IServiceProvider的所有建構函式依賴項.在上面的程式碼中,它會使用Autofac容器來解析產生類。
這樣就能初步的達到我們替換IOC容器的的效果了..
但是,這個操作過程與asp.net MVC的不同之處在於.控制器本身不會從容器中解析出來,所以服務只能從它的構造器引數中解析出來。
所以.這個過程,讓我們無法使用Autofac的一些更高階功能.比如屬性注入(關於屬性注入的好壞..屬於仁者見仁智者見智的東西,這裡我們不討論它是好還是壞.)
2.如何使用Autofac的高階功能,屬性注入.
我們回到Autofac設定程式碼,並設定屬性注入如下:
var containerBuilder = new ContainerBuilder(); //模組化注入 containerBuilder.RegisterModule<DefaultModule>(); //屬性注入控制器 containerBuilder.RegisterType<AutoDIController>().PropertiesAutowired(); containerBuilder.Populate(services);
注入模組的程式碼修改如下:
//屬性注入 builder.RegisterType<TestService>().As<ITestService>().PropertiesAutowired();
然後修改我們的控制器程式碼如下:
public class AutoDIController : BaseController { public ITestService _testService { get; set; } // GET: AutoDI public ActionResult Index() { ViewBag.date = _testService.GetList("Name"); return View(); } }
這裡我們剔除了控制器的建構函式.
我們執行程式碼,會發現_testService 為null,所以根本沒有注入成功.失敗的原因上面我們已經解釋過了...但是還是強調一下吧..
雖然控制器的建構函式依賴性將由MVC從IServiceProvider解決(也就是我們之前建構函式注入的例子),
但是控制器本身的例項(以及它的處理)卻是由框架建立和擁有的,而不是由容器所有。
那麼我們該如何改變控制器本身的建立和所有者呢?
我們會在Microsoft.Extensions.DependencyInjection中找到一個方法.叫做AddControllersAsServices
它的註釋翻譯過來為:將控制器的寄宿器轉為註冊的服務(也就是我們替換的autofac).
但是,注意..這裡雖然是將控制的所有者改成了autofac,但是我們還是不能使用相關的屬性注入方法.
所以,我們到GITHUB上來看看這個方法原始碼如下.(這就是開源的好處...):
public static IMvcBuilder AddControllersAsServices(this IMvcBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } var feature = new ControllerFeature(); builder.PartManager.PopulateFeature(feature); foreach (var controller in feature.Controllers.Select(c => c.AsType())) { builder.Services.TryAddTransient(controller, controller); } builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); return builder; }
我們會發現最後一句..
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
意思是用ServiceBasedControllerActivator替換DefaultControllerActivator(意味著框架現在會嘗試從IServiceProvider中解析控制器例項)
..這下終於真相大白了..
我們只需要修改配置服務的程式碼如下:
public IServiceProvider ConfigureServices(IServiceCollection services) { //替換控制器所有者 services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); services.AddMvc(); services.AddDbContext<BloggingContext>(); services.AddDirectoryBrowser(); var containerBuilder = new ContainerBuilder(); containerBuilder.RegisterModule<DefaultModule>(); //採用屬性注入控制器 containerBuilder.RegisterType<AutoDIController>().PropertiesAutowired(); // containerBuilder.RegisterTypes(feature.Controllers.Select(ti => ti.AsType()).ToArray()).PropertiesAutowired(); containerBuilder.Populate(services); var container = containerBuilder.Build(); return new AutofacServiceProvider(container); }
注意,替換的方法一定要在addMVC之前..
然後我們執行我們的控制器程式碼.效果如圖:
如圖所示,_testService已經被例項化了.說明我們的屬性注入就成功了~