標簽: 依賴註入 Autofac ASPNETCore ASP.NET Core依賴註入解讀&使用Autofac替代實現 1. 前言 2. ASP.NET Core 中的DI方式 3. Autofac實現和自定義實現擴展方法 3.1 安裝Autofac 3.2 創建容器並註冊依賴 4. 參考 ...
標簽: 依賴註入 Autofac ASPNETCore
1. 前言
關於IoC模式(控制反轉)和DI技術(依賴註入),我們已經見過很多的探討,這裡就不再贅述了。比如說必看的Martin Fowler《IoC 容器和 Dependency Injection 模式》,相關資料鏈接都附於文章末尾。其中我非常贊同Artech的說法"控制更多地體現為一種流程的控制",而依賴註入技術讓我們的應用程式實現了鬆散耦合。
ASP.NET Core本身已經集成了一個輕量級的IOC容器,開發者只需要定義好介面後,在Startup.cs的ConfigureServices方法里使用對應生命周期的綁定方法即可,常見方法如下
services.AddTransient<IApplicationService,ApplicationService>
services.AddScoped<IApplicationService,ApplicationService>
services.AddSingleton<IApplicationService,ApplicationService>
對於上述的三種DI註入方式,官方也給出了詳細的解釋,我來簡單翻譯一下
- Transient
Transient 服務在每次請求時被創建,它最好被用於輕量級無狀態服務(如我們的Repository和ApplicationService服務) - Scoped
Scoped 服務在每次請求時被創建,生命周期橫貫整次請求 - Singleton
顧名思義,Singleton(單例) 服務在第一次請求時被創建(或者當我們在ConfigureServices中指定創建某一實例並運行方法),其後的每次請求將沿用已創建服務。如果開發者的應用需要單例服務情景,請設計成允許服務容器來對服務生命周期進行操作,而不是手動實現單例設計模式然後由開發者在自定義類中進行操作。
在這之後,我們便可以將服務通過構造函數註入或者是屬性註入的方式註入到Controller,View(通過使用@inject
),甚至是Filter中(以前使用Unity將依賴註入到Filter真是一種痛苦)。話不多說,先來體驗一把
Tips:Startup.cs是什麼,詳見ASP.NET Core 介紹和項目解讀
2. ASP.NET Core 中的DI方式
大多項目舉例依賴註入的生命周期演示時,都會採取可變Guid來作為返回顯示,此次示例也會這樣處理。我們先定義一個IGuidAppService介面,裡面定義基介面和三種註入模式的介面
public interface IGuidAppService
{
Guid GuidItem();
}
public interface IGuidTransientAppService : IGuidAppService
{
}
public interface IGuidScopedAppService : IGuidAppService
{
}
public interface IGuidSingletonAppService : IGuidAppService
{
}
同樣的,在GuidAppService中定義其實現類。這裡為了直觀顯示每次請求的返回值,採取如下代碼
public class GuidAppServiceBase : IGuidAppService
{
private readonly Guid _item;
public GuidAppServiceBase()
{
_item = Guid.NewGuid();
}
public Guid GuidItem()
{
return _item;
}
}
public class GuidTransientAppService : GuidAppServiceBase, IGuidTransientAppService
{
}
public class GuidScopedAppService : GuidAppServiceBase, IGuidScopedAppService
{
}
public class GuidSingletonAppService : GuidAppServiceBase, IGuidSingletonAppService
{
}
最後是Controller和View視圖的代碼
# Controller
public class HomeController : Controller
{
private readonly IGuidTransientAppService _guidTransientAppService; //#構造函數註入
//private IGuidTransientAppService _guidTransientAppService { get; } #屬性註入
private readonly IGuidScopedAppService _guidScopedAppService;
private readonly IGuidSingletonAppService _guidSingletonAppService;
public HomeController(IGuidTransientAppService guidTransientAppService,
IGuidScopedAppService guidScopedAppService, IGuidSingletonAppService guidSingletonAppService)
{
_guidTransientAppService = guidTransientAppService;
_guidScopedAppService = guidScopedAppService;
_guidSingletonAppService = guidSingletonAppService;
}
public IActionResult Index()
{
ViewBag.TransientItem = _guidTransientAppService.GuidItem();
ViewBag.ScopedItem = _guidScopedAppService.GuidItem();
ViewBag.SingletonItem = _guidSingletonAppService.GuidItem();
return View();
}
}
# Index View
<div class="row">
<div >
<h2>GuidItem Shows</h2>
<h3>TransientItem: @ViewBag.TransientItem</h3>
<h3>ScopedItem: @ViewBag.ScopedItem</h3>
<h3>SingletonItem: @ViewBag.SingletonItem</h3>
</div>
</div>
之後我們打開兩個瀏覽器,分別刷新數次,也只會發現“TransientItem”和“ScopedItem”的數值不斷變化,“SingletonItem”欄的數值是不會有任何變化的,這就體現出單例模式的作用了,示例圖如下
但是這好像還不夠,要知道我們的Scoped的解讀是“生命周期橫貫整次請求”,但是現在演示起來和Transient好像沒有什麼區別(因為兩個頁面每次瀏覽器請求仍然是獨立的,並不包含於一次中),所以我們採用以下代碼來演示下(同一請求源)
# 新建GuidItemPartial.cshtml視圖,複製如下代碼,使用@inject註入依賴
@using DependencyInjection.IApplicationService
@inject IGuidTransientAppService TransientAppService
@inject IGuidScopedAppService GuidScopedAppServic
@inject IGuidSingletonAppService GuidSingletonAppService
<div class="row">
<div>
<h2>GuidItem Shows</h2>
<h3>TransientItem: @TransientAppService.GuidItem()</h3>
<h3>ScopedItem: @GuidScopedAppServic.GuidItem()</h3>
<h3>SingletonItem: @GuidSingletonAppService.GuidItem()</h3>
</div>
</div>
# 原先的index視圖
@{
ViewData["Title"] = "Home Page";
}
@Html.Partial("GuidItemPartial")
@Html.Partial("GuidItemPartial")
依然是 Ctrl+F5 調試運行,可以發現“ScopedItem”在同一請求源中是不會發生變化的,但是“TransientItem”依然不斷變化,理論仍然是支持的
3. Autofac實現和自定義實現擴展方法
除了ASP.NETCore自帶的IOC容器外,我們還可以使用其他成熟的DI框架,如Autofac,StructureMap等(筆者只用過Unity,Ninject和Castle,Castle也是使用ABP時自帶的)。
3.1 安裝Autofac
首先在project.json的dependency
節點中加入Autofac.Extensions.DependencyInjection
引用,目前最新版本是4.0.0-rc3-309
3.2 創建容器並註冊依賴
在Startup.cs中創建一個public IContainer ApplicationContainer { get; private set; }
對象,並把ConfigureServices
返回類型改為IServiceProvider
,然後複製以下代碼進去,也可以實現相關功能
var builder = new ContainerBuilder();
//註意以下寫法
builder.RegisterType<GuidTransientAppService>().As<IGuidTransientAppService>();
builder.RegisterType<GuidScopedAppService>().As<IGuidScopedAppService>().InstancePerLifetimeScope();
builder.RegisterType<GuidSingletonAppService>().As<IGuidSingletonAppService>().SingleInstance();
builder.Populate(services);
this.ApplicationContainer = builder.Build();
return new AutofacServiceProvider(this.ApplicationContainer);
值得註意的幾點:
- 創建Autofac容器時不要忘了將
ConfigureServices
的返回值修改為IServiceProvider
- 對應ASP.NET Core提及的不同的生命周期,Autofac也定義了對應的擴展方法,如InstancePerLifetimeScope等,預設為Transient模式,包括EntityFramwork等Context也是該種模式
- Autofac Core不支持從View中註入,但是可以和ASP.NET Core自帶IOC容器配合使用
- Autofac Core版本和傳統的ASP.NET MVC項目版本的區別
4. 參考鏈接
- IoC 容器和 Dependency Injection 模式
- 控制反轉—維基百科
- DependencyInjection-GitHub
- ASP.NET Core中的依賴註入(4): 構造函數的選擇與服務生命周期管理
- Dependency Injectionf!