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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...