ASP.NET Core依賴注入解讀&使用Autofac替代實現

標籤: 依賴注入 Autofac ASPNETCorehtml


1. 前言

關於IoC模式(控制反轉)和DI技術(依賴注入),咱們已經見過不少的探討,這裏就再也不贅述了。好比說必看的Martin Fowler《IoC 容器和 Dependency Injection 模式》,相關資料連接都附於文章末尾。其中我很是贊同Artech的說法"控制更多地體現爲一種流程的控制",而依賴注入技術讓咱們的應用程序實現了鬆散耦合。git

ASP.NET Core自己已經集成了一個輕量級的IOC容器,開發者只須要定義好接口後,在Startup.cs的ConfigureServices方法裏使用對應生命週期的綁定方法便可,常見方法以下github

services.AddTransient<IApplicationService,ApplicationService>

services.AddScoped<IApplicationService,ApplicationService>

services.AddSingleton<IApplicationService,ApplicationService>

對於上述的三種DI注入方式,官方也給出了詳細的解釋,我來簡單翻譯一下json

  • 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中定義其實現類。這裏爲了直觀顯示每次請求的返回值,採起以下代碼asp.net

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視圖的代碼ide

# 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);

值得注意的幾點:

  1. 建立Autofac容器時不要忘了將ConfigureServices的返回值修改成IServiceProvider
  2. 對應ASP.NET Core說起的不一樣的生命週期,Autofac也定義了對應的擴展方法,如InstancePerLifetimeScope等,默認爲Transient模式,包括EntityFramwork等Context也是該種模式
  3. Autofac Core不支持從View中注入,可是能夠和ASP.NET Core自帶IOC容器配合使用
  4. Autofac Core版本和傳統的ASP.NET MVC項目版本的區別

4. 參考連接

相關文章
相關標籤/搜索