ASP.NET Core 2 學習筆記(四)

来源:https://www.cnblogs.com/snaildev/archive/2018/05/24/9081760.html
-Advertisement-
Play Games

ASP.NET Core使用了大量的依賴註入(Dependency Injection, DI),把控制反轉(Inversion Of Control, IoC)運用的相當巧妙。DI可算是ASP.NET Core最精華的一部分,有用過Autofac或類似的DI Framework對此應該不陌生。本篇 ...


ASP.NET Core使用了大量的依賴註入(Dependency Injection, DI),把控制反轉(Inversion Of Control, IoC)運用的相當巧妙。DI可算是ASP.NET Core最精華的一部分,有用過Autofac或類似的DI Framework對此應該不陌生。
本篇將介紹ASP.NET Core的依賴註入(Dependency Injection)。

DI 容器介紹

在沒有使用DI Framework 的情況下,假設在UserController 要調用UserLogic,會直接在UserController 實例化UserLogic,如下:

public class UserLogic {
    public void Create(User user) {
        // ...
    }
}

public class UserController : Controller {
    public void Register(User user){
        var logic = new UserLogic();
        logic.Create(user);
        // ...
    }
}

以上程式基本沒什麼問題,但是依賴關係差了點。UserController 必須要依賴UserLogic才可以運行,拆出介面改成:

public interface IUserLogic {
    void Create(User user);
}

public class UserLogic : IUserLogic {
    public void Create(User user) {
        // ...
    }
}

public class UserController : Controller {
    private readonly IUserLogic _userLogic;

    public UserController() {
        _userLogic = new UserLogic();
    }

    public void Register(User user){
        _userLogic.Create(user);
        // ...
    }
}

UserController 與UserLogic 的依賴關係只是從Action 移到構造方法中,依然還是很強的依賴關係。

ASP.NET Core透過DI容器,切斷這些依賴關係,實例的產生不在使用方(指上例UserController構造方法中的new),而是在DI容器。
DI容器的註冊方式也很簡單,在Startup.ConfigureServices註冊。如下:

Startup.cs

// ...
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }
}

services就是一個DI容器。
此例把MVC的服務註冊到DI容器,等到需要用到MVC服務時,才從DI容器取得對象實例。 

基本上要註入到Service的類沒什麼限制,除了靜態類
以下示例就只是一般的Class繼承Interface:

public interface ISample
{
    int Id { get; }
}

public class Sample : ISample
{
    private static int _counter;
    private int _id;

    public Sample()
    {
        _id = ++_counter;
    }

    public int Id => _id;
}

要註入的Service需要在Startup.ConfigureServices中註冊實例類型。如下:

Startup.cs

// ...
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // ...
        services.AddScoped<ISample, Sample>();
    }
}
  • 第一個泛型為註入的類型
    建議用Interface來包裝,這樣在才能把依賴關係拆除。
  • 第二個泛型為實例的類型

DI 運行方式

ASP.NET Core的DI是採用Constructor Injection,也就是說會把實例化的物件從建構子傳入。
如果要取用DI容器內的物件,只要在建構子加入相對的Interface即可。例如:

Controllers\HomeController.cs

using Microsoft.AspNetCore.Mvc;
using MyWebsite.Models;

namespace MyWebsite.Controllers
{
    public class HomeController : Controller
    {
        private readonly ISample _sample;

        public HomeController(ISample sample)
        {
            _sample = sample;
        }

        public string Index()
        {
            return $"[ISample]\r\n"
                 + $"Id: {_sample.Id}\r\n"
                 + $"HashCode: {_sample.GetHashCode()}\r\n"
                 + $"Tpye: {_sample.GetType()}";
        }
    }
}

輸出內容如下:

[ISample]
Id: 1
HashCode: 52582687
Tpye: MyWebsite.Models.Sample

ASP.NET Core 實例化Controller 時,發現構造方法中有ISample 這個類型的參數,就把Sample 的實例註入給該Controller。

每個Request 都會把Controller 實例化,所以DI 容器會從構造方法註入ISample 的實例,把sample 存到變數_sample 中,就能確保Action 能夠使用到被註入進來的ISample 實例。

註入實例過程,情境如下:

Service 生命周期

註冊在DI 容器的Service 分三種生命周期:

  • Transient
    每次註入時,都重新new一個新的實例。
  • Scoped
    每個Request都重新new一個新的實例,同一個Request不管經過多少個Pipeline都是用同一個實例。上例所使用的就是Scoped。
  • Singleton
    被實例化後就不會消失,程式運行期間只會有一個實例。

小改一下Sample 類的代碼:

namespace MyWebsite.Models
{
    public interface ISample
    {
        int Id { get; }
    }

    public interface ISampleTransient : ISample
    {
    }

    public interface ISampleScoped : ISample
    {
    }

    public interface ISampleSingleton : ISample
    {
    }

    public class Sample : ISampleTransient, ISampleScoped, ISampleSingleton
    {
        private static int _counter;
        private int _id;

        public Sample()
        {
            _id = ++_counter;
        }

        public int Id => _id;
    }
}

Startup.ConfigureServices中註冊三種不同生命周期的服務。如下:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<ISampleTransient, Sample>();
        services.AddScoped<ISampleScoped, Sample>();
        services.AddSingleton<ISampleSingleton, Sample>();
        // Singleton 也可以用以下方法註冊
        // services.AddSingleton<ISampleSingleton>(new Sample());
    }
}

Service Injection

只要是透過WebHost產生實例的類型,都可以在構造方法註入。

所以Controller、View、Filter、Middleware或自定義的Service等都可以被註入。
此篇我只用Controller、View、Service做為範例。

Controller

在TestController 中註入上例的三個Services:

Controllers\TestController.cs

using Microsoft.AspNetCore.Mvc;
using MyWebsite.Models;

namespace MyWebsite.Controllers
{
    public class TestController : Controller
    {
        private readonly ISample _transient;
        private readonly ISample _scoped;
        private readonly ISample _singleton;

        public TestController(
            ISampleTransient transient,
            ISampleScoped scoped,
            ISampleSingleton singleton)
        {
            _transient = transient;
            _scoped = scoped;
            _singleton = singleton;
        }

        public IActionResult Index()
        {
            ViewBag.TransientId = _transient.Id;
            ViewBag.TransientHashCode = _transient.GetHashCode();

            ViewBag.ScopedId = _scoped.Id;
            ViewBag.ScopedHashCode = _scoped.GetHashCode();

            ViewBag.SingletonId = _singleton.Id;
            ViewBag.SingletonHashCode = _singleton.GetHashCode();
            return View();
        }
    }
}

Views\Test\Index.cshtml

<table border="1">
    <tr><td colspan="3">Cotroller</td></tr>
    <tr><td>Lifetimes</td><td>Id</td><td>Hash Code</td></tr>
    <tr><td>Transient</td><td>@ViewBag.TransientId</td><td>@ViewBag.TransientHashCode</td></tr>
    <tr><td>Scoped</td><td>@ViewBag.ScopedId</td><td>@ViewBag.ScopedHashCode</td></tr>
    <tr><td>Singleton</td><td>@ViewBag.SingletonId</td><td>@ViewBag.SingletonHashCode</td></tr>
</table>

輸出內容如下:

 

從左到又打開頁面三次,可以發現Singleton的Id及HashCode都是一樣的,現在還看不太能看出來Transient及Scoped的差異。

Service 實例產生方式:

 

圖例說明:

  • A為Singleton對象實例
    一但實例化,就會一直存在於DI容器中。
  • B為Scoped對象實例
    每次Request就會產生新的實例在DI容器中,讓同Request周期的使用方,拿到同一個實例。
  • CTransient對象實例
    只要跟DI容器請求這個類型,就會取得新的實例。

View

View註入Service的方式,直接在*.cshtml使用@inject

Views\Test\Index.cshtml

@using MyWebsite.Models

@inject ISampleTransient transient
@inject ISampleScoped scoped
@inject ISampleSingleton singleton

<table border="1">
    <tr><td colspan="3">Cotroller</td></tr>
    <tr><td>Lifetimes</td><td>Id</td><td>Hash Code</td></tr>
    <tr><td>Transient</td><td>@ViewBag.TransientId</td><td>@ViewBag.TransientHashCode</td></tr>
    <tr><td>Scoped</td><td>@ViewBag.ScopedId</td><td>@ViewBag.ScopedHashCode</td></tr>
    <tr><td>Singleton</td><td>@ViewBag.SingletonId</td><td>@ViewBag.SingletonHashCode</td></tr>
</table>
<hr />
<table border="1">
    <tr><td colspan="3">View</td></tr>
    <tr><td>Lifetimes</td><td>Id</td><td>Hash Code</td></tr>
    <tr><td>Transient</td><td>@transient.Id</td><td>@transient.GetHashCode()</td></tr>
    <tr><td>Scoped</td><td>@scoped.Id</td><td>@scoped.GetHashCode()</td></tr>
    <tr><td>Singleton</td><td>@singleton.Id</td><td>@singleton.GetHashCode()</td></tr>
</table>

輸出內容如下:

從左到又打開頁面三次,Singleton的Id及HashCode如前例是一樣的。
Transient及Scoped的差異在這次就有明顯差異,Scoped在同一次Request的Id及HashCode都是一樣的,如紅紫籃框。

Service

簡單建立一個CustomService,註入上例三個Service,代碼類似TestController。如下:

Services\CustomService.cs

using MyWebsite.Models;

namespace MyWebsite.Services
{
    public class CustomService
    {
        public ISample Transient { get; private set; }
        public ISample Scoped { get; private set; }
        public ISample Singleton { get; private set; }

        public CustomService(ISampleTransient transient,
            ISampleScoped scoped,
            ISampleSingleton singleton)
        {
            Transient = transient;
            Scoped = scoped;
            Singleton = singleton;
        }
    }
}

註冊CustomService

Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // ...
        services.AddScoped<CustomService, CustomService>();
    }
}

第一個泛型也可以是類,不一定要是介面。
缺點是使用方以Class作為依賴關係,變成強關聯的依賴。

在View 註入CustomService:

Views\Home\Index.cshtml

@using MyWebsite
@using MyWebsite.Models
@using MyWebsite.Services

@inject ISampleTransient transient
@inject ISampleScoped scoped
@inject ISampleSingleton singleton
@inject CustomService customService

<table border="1">
    <tr><td colspan="3">Cotroller</td></tr>
    <tr><td>Lifetimes</td><td>Id</td><td>Hash Code</td></tr>
    <tr><td>Transient</td><td>@ViewBag.TransientId</td><td>@ViewBag.TransientHashCode</td></tr>
    <tr><td>Scoped</td><td>@ViewBag.ScopedId</td><td>@ViewBag.ScopedHashCode</td></tr>
    <tr><td>Singleton</td><td>@ViewBag.SingletonId</td><td>@ViewBag.SingletonHashCode</td></tr>
</table>
<hr />
<table border="1">
    <tr><td colspan="3">View</td></tr>
    <tr><td>Lifetimes</td><td>Id</td><td>Hash Code</td></tr>
    <tr><td>Transient</td><td>@transient.Id</td><td>@transient.GetHashCode()</td></tr>
    <tr><td>Scoped</td><td>@scoped.Id</td><td>@scoped.GetHashCode()</td></tr>
    <tr><td>Singleton</td><td>@singleton.Id</td><td>@singleton.GetHashCode()</td></tr>
</table>
<hr />
<table border="1">
    <tr><td colspan="3">Custom Service</td></tr>
    <tr><td>Lifetimes</td><td>Id</td><td>Hash Code</td></tr>
    <tr><td>Transient</td><td>@customService.Transient.Id</td><td>@customService.Transient.GetHashCode()</td></tr>
    <tr><td>Scoped</td><td>@customService.Scoped.Id</td><td>@customService.Scoped.GetHashCode()</td></tr>
    <tr><td>Singleton</td><td>@customService.Singleton.Id</td><td>@customService.Singleton.GetHashCode()</td></tr>
</table>

輸出內容如下:

 

從左到又打開頁面三次:

  • Transient
    如預期,每次註入都是不一樣的實例。
  • Scoped
    在同一個Requset中,不論是在哪邊被註入,都是同樣的實例。
  • Singleton
    不管Requset多少次,都會是同一個實例。

參考

Introduction to Dependency Injection in ASP.NET Core 
ASP.NET Core Dependency Injection Deep Dive

 

老司機發車啦:https://github.com/SnailDev/SnailDev.NETCore2Learning


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 百度,一家讓人既愛又恨的企業,血友吧貼吧被賣,魏則西事件的持續發酵,一時間將百度推到了輿論的風口浪尖上。是非對錯,我們在這裡也不多做評判,本文呢為大家整理了百度開源的70+項目,看看有沒有感興趣的。本文內容綜合整理自oschina、github。 1. JavaScript圖表庫 ECharts E ...
  • 為了減少由於單個請求掛掉而拖垮整站的情況發生,給所有請求做統計是一個不錯的解決方法,通過觀察哪些請求的耗時比較長,我們就可以找到對應的介面、代碼、數據表,做有針對性的優化可以提高效率。在 asp.net web api 中我們可以通過註冊一個 DelegatingHandler 來實現該功能。那在  ...
  • 1、什麼是編程語言? 程式員與電腦溝通的介質 2、什麼是編程? 程式員基於某種編程語言的語法格式將想讓電腦所做的事寫到文件中讓電腦執行,編程的結果就是文件,文件的內容就是程式; 3、為什麼要編程? 讓電腦代替人類工作,解放人力 4,、電腦硬體基礎 (1)、什麼是x86-64位? x86是c ...
  • 正確開啟虛擬化的方式 列表如ListBox,ListView,TreeView,GridView等,開啟虛擬化 直接在模板中,設置CanContentScroll="True" 如模板中未設置CanContentScroll屬性,可以在列表添加屬性ScrollViewer.CanContentScr ...
  • 回到目錄 Invoke和BeginInvoke都是調用委托實體的方法,前者是同步調用,即它運行在主線程上,當Invode處理時間長時,會出現阻塞的情況,而BeginInvod是非同步操作,它會從新開啟一個線程,所以不會租塞主線程,在使用BeginInvoke時,如果希望等待執行的結果 ,可以使用End ...
  • .Net Framework中,把資源分為托管資源和非托管資源兩大類, 托管資源指可以通過.Net Frame垃圾回收器進行回收的資源,主要是指分配在托管堆上你的記憶體資源,這類資源的回收是不需要人工干預,.Net Framework的垃圾回收器會在合適的時刻進行回收,程式也可以主動調用GC.Coll ...
  • 用過C/C++的人都知道有個union,特別好用,似乎char數組到short,int,float等的轉換無所不能,也確實是能,並且用起來十分方便。那C#為什麼沒有這個關鍵字呢?怎麼實現這個功能?其實C#只是沒有了這個關鍵字,但是功能是能實現的,而且也是非常方便,並且是安全的。網上有人用Struct ...
  • 基本上所有的人都在DateTime類型的欄位,被序列化成json的時候,遇到過可恨的Date(1294499956278+0800);但是又苦於不能全局格式化設置,比較難受。以往的方式,要麼使用全局的Newtonsoft的配置,要麼自己重寫ActionResult,總之都比較麻煩。在Core提供了更 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...