Dependency Injection vào Views là gì?

ASP.NET Core hỗ trợ dependency injection (DI) vào views. Điều này hữu ích cho các view-specific services như localization hoặc data chỉ dùng để populate view elements.
Lưu ý: Hầu hết data mà views hiển thị nên được truyền từ controller. @inject chỉ nên dùng cho view-specific services.

Configuration Injection

Inject Configuration vào View

Giá trị từ settings files như appsettings.jsonappsettings.Development.json có thể được inject vào view.

appsettings.Development.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "MyRoot": {
    "MyParent": {
      "MyChildName": "Joe"
    }
  }
}

MVC View

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

@{
    ViewData["Title"] = "Privacy MVC";
}

<h1>@ViewData["Title"]</h1>

<p>MVC Use this page to detail your site's privacy policy.</p>

<h2>
    MyRoot:MyParent:MyChildName:
    @Configuration["MyRoot:MyParent:MyChildName"]
</h2>

Service Injection với @inject

Cú pháp

Sử dụng directive @inject để inject service vào view:
@using System.Threading.Tasks
@using ViewInjectSample.Model
@using ViewInjectSample.Model.Services

@model IEnumerable<ToDoItem>
@inject StatisticsService StatsService

Ví dụ: ToDo List với Statistics

View này hiển thị danh sách ToDoItem cùng với statistics summary từ injected StatisticsService:
@using System.Threading.Tasks
@using ViewInjectSample.Model
@using ViewInjectSample.Model.Services

@model IEnumerable<ToDoItem>
@inject StatisticsService StatsService

<!DOCTYPE html>
<html>
<head>
    <title>To Do Items</title>
</head>
<body>
    <div>
        <h1>To Do Items</h1>

        <ul>
            <li>Total Items: @StatsService.GetCount()</li>
            <li>Completed: @StatsService.GetCompletedCount()</li>
            <li>Avg. Priority: @StatsService.GetAveragePriority()</li>
        </ul>

        <table>
            <tr>
                <th>Name</th>
                <th>Priority</th>
                <th>Is Done?</th>
            </tr>
            @foreach (var item in Model)
            {
                <tr>
                    <td>@item.Name</td>
                    <td>@item.Priority</td>
                    <td>@item.IsDone</td>
                </tr>
            }
        </table>
    </div>
</body>
</html>

Đăng ký Service trong Program.cs

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

builder.Services.AddTransient<IToDoItemRepository, ToDoItemRepository>();
builder.Services.AddTransient<StatisticsService>();
builder.Services.AddTransient<ProfileOptionsService>();

StatisticsService Implementation

using System.Linq;
using ViewInjectSample.Interfaces;

namespace ViewInjectSample.Model.Services
{
    public class StatisticsService
    {
        private readonly IToDoItemRepository _toDoItemRepository;

        public StatisticsService(IToDoItemRepository toDoItemRepository)
        {
            _toDoItemRepository = toDoItemRepository;
        }

        public int GetCount() => _toDoItemRepository.List().Count();

        public int GetCompletedCount() =>
            _toDoItemRepository.List().Count(x => x.IsDone);

        public double GetAveragePriority()
        {
            var items = _toDoItemRepository.List();
            if (items.Count() == 0) return 0.0;
            return items.Average(x => x.Priority);
        }
    }
}

Populating Lookup Data

Vấn đề truyền thống

Rendering form với dropdown lists thường yêu cầu controller:
  1. Request data access services cho mỗi tập options
  2. Populate model hoặc ViewBag với các options

Giải pháp: Inject Service vào View

public class ProfileController : Controller
{
    public IActionResult Index()
    {
        // Chỉ cần truyền profile instance
        var profile = new Profile
        {
            Name = "Rick",
            FavColor = "Blue",
            Gender = "Male",
            State = new State("Ohio", "OH")
        };
        return View(profile);
    }
}
@using System.Threading.Tasks
@using ViewInjectSample.Model.Services

@model ViewInjectSample.Model.Profile
@inject ProfileOptionsService Options

<!DOCTYPE html>
<html>
<head>
    <title>Update Profile</title>
</head>
<body>
    <div>
        <h1>Update Profile</h1>

        Name: @Html.TextBoxFor(m => m.Name)
        <br />

        Gender: @Html.DropDownList("Gender",
            Options.ListGenders().Select(g =>
                new SelectListItem { Text = g, Value = g }))
        <br />

        State: @Html.DropDownListFor(m => m.State!.Code,
            Options.ListStates().Select(s =>
                new SelectListItem { Text = s.Name, Value = s.Code }))
        <br />

        Fav. Color: @Html.DropDownList("FavColor",
            Options.ListColors().Select(c =>
                new SelectListItem { Text = c, Value = c }))
    </div>
</body>
</html>

ProfileOptionsService

namespace ViewInjectSample.Model.Services
{
    public class ProfileOptionsService
    {
        public List<string> ListGenders()
        {
            return new List<string>() { "Female", "Male" };
        }

        public List<State> ListStates()
        {
            return new List<State>
            {
                new State("Alabama", "AL"),
                new State("Alaska", "AK"),
                new State("Ohio", "OH")
            };
        }

        public List<string> ListColors()
        {
            return new List<string>() { "Blue", "Green", "Red", "Yellow" };
        }
    }
}
Lưu ý: Unregistered type sẽ throw exception tại runtime vì service provider được query nội bộ qua GetRequiredService.

Overriding Services

Default Fields có sẵn

View có sẵn các default fields:
FieldMô tả
HtmlHTML Helpers
ComponentView Components
UrlURL Helpers

Override Html Helper

Sử dụng @inject để override default HTML Helpers với phiên bản custom:
@using System.Threading.Tasks
@using ViewInjectSample.Helpers

@inject MyHtmlHelper Html

<!DOCTYPE html>
<html>
<head>
    <title>My Helper</title>
</head>
<body>
    <div>
        Test: @Html.Value
    </div>
</body>
</html>

So sánh: Controller DI vs View DI

Tiêu chíController DIView DI (@inject)
Mục đíchBusiness logic, data accessView-specific services
Cú phápConstructor injection@inject directive
TestabilityCaoThấp hơn
Khuyến nghị✅ Ưu tiên dùngHạn chế, chỉ khi cần

Tài liệu tham khảo