ASP.NET Core Constructor Depedency Injection (DI)


  1. 說明
    1. 建立介面類別
    2. 建立實作類別
    3. 向容器註冊
    4. 注入類別建構子
    5. 使用類別

筆記在 ASP.NET Core 經常使用的預設 DI Container 以及從撰寫 Interface 到使用 Class 的流程。

logo

說明

在 ASP.NET Core 開始,不論是 MVC 專案或者是 Razor Pages 專案,預設上就必須使用 DI Container,而不再像是 .NET Framework 時代,需要自行使用第三方的 DI Container 來實作依賴注入 (Dependency Injection) 的設計模式。

本次比次 ASP.NET Core 依照下列步驟,完成預設依賴注入的使用,本次範例說明的是智慧推薦餐點的服務 (Suggestion.cs)。

建立介面類別

關鍵的第一步就是要設計介面,讓整個系統的依賴於抽象 (abstract),而非依賴於實作 (concrete)。

在專案中加入 Services 資料夾,並建立 ISuggestion.cs

介面當中只有一個函式簽名,就是會回傳字串的函式 Suggest

namespace CodeRestaurant.Services
{
    public interface ISuggestion
    {
        public string Suggest();
    }
}

建立實作類別

Services 資料夾,建立實作 ISuggestion 的兩個實作類別:

Services/Suggestion.cs

namespace CodeRestaurant.Services
{
    public class Suggestion : ISuggestion
    {
        public string Suggest()
        {
            var meals = new List<string> { 
              "漢堡", 
              "義大利麵", 
              "拉麵", 
              "叉燒飯", 
              "咖哩飯" 
            };
            var random = new Random();
            return meals[random.Next(0, meals.Count - 1)];
        }
    }
}

Services/HamburgeSuggestion.cs

namespace CodeRestaurant.Services
{
    public class HamburgerSuggestion : ISuggestion
    {
        public string Suggest()
        {
            return DateTime.Now.DayOfWeek == DayOfWeek.Sunday ? 
                "牛肉漢堡" : "豬排漢堡";
        }
    }
}

向容器註冊

接著在容器中註冊介面以及要使用的類別。

Program.cs

using CodeRestaurant.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddSingleton<ISuggestion, Suggestion>();
var app = builder.Build();

注入類別建構子

本次範例專案是以 Razor Pages 為例,因此使用其中一個 Page 來示範。

DI 容器會將所註冊的類別傳入建構子,因此只須在類別模型中,將類別注入到 ISuggestion 型別的內部成員 _suggestion,就可以在程式中使用,例如在 OnGet 就可以使用介面的方法 Suggest 得到智慧推薦的餐點。

Index.cshtml.cs

using CodeRestaurant.Services;

namespace CodeRestaurant.Pages
{
    public class IndexModel : PageModel
    {
        private readonly ISuggestion _suggestion;
        public string Dinner { get; set; }

        public IndexModel(ISuggestion suggestion)
        {
            _suggestion = suggestion;
        }

        public void OnGet()
        {
            Dinner = _suggestion.Suggest();
        }
    }
}

使用類別

在頁面中,因為有先在 IndexModel 定義自動變數 Dinner,只需要在 Razor 中使用 @Model.Dinner 即可使用注入的類別的運算結果。

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="border border-1 border-primary p-3 display-1">
    @Model.Dinner
</div>

而依賴注入的優點為何?答案就是更好的擴充與維護性。

當開發人員要替換掉原本的餐點推薦函式時,例如由原本的 Suggestion.cs 改為 HamburgerSuggestion.cs 的時候,

只要在 DI 容器調整所要注入的類別即可。

Program.cs 調整前

builder.Services.AddRazorPages();
builder.Services.AddSingleton<ISuggestion, Suggestion>();
var app = builder.Build();

Program.cs 調整後

builder.Services.AddRazorPages();
builder.Services.AddSingleton<ISuggestion, HamburgerSuggestion>();
var app = builder.Build();