ASP.NET Core의 보기에 종속성 주입

ASP.NET Core는 보기에 대한 종속성 주입을 지원합니다. 이는 보기 요소를 채우는 데만 필요한 지역화 또는 데이터 같은 보기 관련 서비스에 유용합니다. 보기에서 표시하는 데이터 대부분은 컨트롤러에서 전달되어야 합니다.

샘플 코드 보기 및 다운로드(다운로드 방법)

구성 주입

appsettings.jsonappsettings.Development.json 같은 설정 파일 값을 보기에 삽입할 수 있습니다. 샘플 코드appsettings.Development.json을 고려하세요.

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

다음 표시는 Razor 페이지 보기에 구성 값을 표시합니다.

@page
@model PrivacyModel
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
    ViewData["Title"] = "Privacy RP";
}
<h1>@ViewData["Title"]</h1>

<p>PR Privacy</p>

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

다음 표시는 MVC 보기에 구성 값을 표시합니다.

@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>

자세한 내용은 ASP.NET Core의 구성을 참조하세요.

서비스 주입

@inject 지시문을 사용하여 서비스를 보기에 주입할 수 있습니다.

@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>

이 보기는 전반적인 통계를 보여주는 요약 정보와 함께 ToDoItem 인스턴스 목록을 표시합니다. 요약 정보는 주입된 StatisticsService에서 채워집니다. 이 서비스는 Program.csConfigureServices에서 종속성 주입을 위해 등록됩니다.

using ViewInjectSample.Helpers;
using ViewInjectSample.Infrastructure;
using ViewInjectSample.Interfaces;
using ViewInjectSample.Model.Services;

var builder = WebApplication.CreateBuilder(args);

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

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

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.MapRazorPages();

app.MapDefaultControllerRoute();


app.Run();

StatisticsService는 리포지토리를 통해 액세스되는 ToDoItem 인스턴스 집합에서 몇 가지 계산을 수행합니다.

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()
        {
            return _toDoItemRepository.List().Count();
        }

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

        public double GetAveragePriority()
        {
            if (_toDoItemRepository.List().Count() == 0)
            {
                return 0.0;
            }

            return _toDoItemRepository.List().Average(x => x.Priority);
        }
    }
}

예제 리포지토리는 메모리 내 컬렉션을 사용합니다. 메모리 내 구현은 원격으로 액세스하는 대규모 데이터 세트에 사용하면 안 됩니다.

이 예제는 보기에 바인딩된 모델과 보기에 주입된 서비스의 데이터를 표시합니다.

To Do view listing total items, completed items, average priority, and a list of tasks with their priority levels and boolean values indicating completion.

조회 데이터 채우기

보기 주입은 드롭다운 목록 같은 UI 요소의 옵션을 채우는 데 유용합니다. 성별, 상태 및 기타 기본 설정을 지정하는 옵션이 포함된 사용자 프로필 양식을 고려해보세요. 표준 접근 방식을 사용하여 이러한 양식을 렌더링하려면 컨트롤러 또는 Razor Page에서 다음을 수행해야 할 수 있습니다.

  • 각 옵션 집합에 대한 데이터 액세스 서비스를 요청합니다.
  • 바인딩할 각 옵션 집합으로 모델 또는 ViewBag를 채웁니다.

다른 방법은 서비스를 보기에 직접 주입하여 옵션을 가져오는 것입니다. 이렇게 하면 컨트롤러 또는 razor Page에 필요한 코드 양이 최소화되어 이 보기 요소 생성 논리를 보기 자체로 이동시킵니다. 프로필 편집 양식을 표시하는 컨트롤러 작업 또는 Razor 페이지는 프로필 인스턴스 양식만 전달하면 됩니다.

using Microsoft.AspNetCore.Mvc;
using ViewInjectSample.Model;

namespace ViewInjectSample.Controllers;

public class ProfileController : Controller
{
    public IActionResult Index()
    {
        // A real app would up profile based on the user.
        var profile = new Profile()
        {
            Name = "Rick",
            FavColor = "Blue",
            Gender = "Male",
            State = new State("Ohio","OH")
        };
        return View(profile);
    }
}

이러한 기본 설정을 업데이트 하기 위한 HTML 양식은 세 가지 속성의 드롭다운 목록을 포함합니다.

Update Profile view with a form allowing the entry of name, gender, state, and favorite Color.

이러한 목록은 보기에 주입된 서비스에 의해서 채워집니다.

@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는 이 양식에 필요한 데이터만 제공하도록 설계된 UI 수준 서비스입니다.

namespace ViewInjectSample.Model.Services;

public class ProfileOptionsService
{
    public List<string> ListGenders()
    {
        // Basic sample
        return new List<string>() {"Female", "Male"};
    }

    public List<State> ListStates()
    {
        // Add a few states
        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" };
    }
}

서비스 공급자가 GetRequiredService를 통해 내부적으로 쿼리되기 때문에 등록되지 않은 형식은 런타임 시 예외를 throw합니다.

서비스 재정의

새 서비스 주입 외에도, 이 기술은 페이지에 이전에 주입된 서비스를 재정의하는 데 사용할 수도 있습니다. 아래 그림은 첫 번째 예에서 사용된 페이지에서 사용 가능한 모든 필드를 보여줍니다.

Intellisense contextual menu on a typed @ symbol listing Html, Component, StatsService, and Url fields

기본 필드에는 Html, ComponentUrl이 포함됩니다. 기본 HTML 도우미를 사용자 지정 버전으로 바꾸려면 @inject를 사용하세요.

@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>

참고 항목

ASP.NET Core는 보기에 대한 종속성 주입을 지원합니다. 이는 보기 요소를 채우는 데만 필요한 지역화 또는 데이터 같은 보기 관련 서비스에 유용합니다. 컨트롤러와 보기 간에 문제를 분리해야 합니다. 보기에서 표시하는 대부분의 데이터는 컨트롤러에서 전달되어야 합니다.

샘플 코드 보기 및 다운로드(다운로드 방법)

구성 주입

appsettings.json 값을 보기에 직접 주입할 수 있습니다.

appsettings.json 파일의 예:

{
   "root": {
      "parent": {
         "child": "myvalue"
      }
   }
}

@inject에 대한 구문: @inject <type> <name>

@inject를 사용하는 예제:

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
   string myValue = Configuration["root:parent:child"];
   ...
}

서비스 주입

@inject 지시문을 사용하여 서비스를 보기에 주입할 수 있습니다. @inject를 보기에 속성을 추가하고 DI를 사용하여 속성을 채우는 것으로 생각하셔도 좋습니다.

@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>

이 보기는 전반적인 통계를 보여주는 요약 정보와 함께 ToDoItem 인스턴스 목록을 표시합니다. 요약 정보는 주입된 StatisticsService에서 채워집니다. 이 서비스는 Startup.csConfigureServices에서 종속성 주입을 위해 등록됩니다.

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

StatisticsService는 리포지토리를 통해 액세스되는 ToDoItem 인스턴스 집합에서 몇 가지 계산을 수행합니다.

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()
        {
            return _toDoItemRepository.List().Count();
        }

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

        public double GetAveragePriority()
        {
            if (_toDoItemRepository.List().Count() == 0)
            {
                return 0.0;
            }

            return _toDoItemRepository.List().Average(x => x.Priority);
        }
    }
}

예제 리포지토리는 메모리 내 컬렉션을 사용합니다. 위에서 살펴본 구현은 메모리 내의 모든 데이터를 대상으로 작동하므로 원격으로 액세스되는 대규모 데이터 집합에는 권장하지 않습니다.

이 예제는 보기에 바인딩된 모델과 보기에 주입된 서비스의 데이터를 표시합니다.

To Do view listing total items, completed items, average priority, and a list of tasks with their priority levels and boolean values indicating completion.

조회 데이터 채우기

보기 주입은 드롭다운 목록 같은 UI 요소의 옵션을 채우는 데 유용합니다. 성별, 상태 및 기타 기본 설정을 지정하는 옵션이 포함된 사용자 프로필 양식을 고려해보세요. 표준 MVC 방식을 사용하여 이러한 양식을 렌더링하려면 컨트롤러에서 이러한 각 옵션 집합에 대한 데이터 액세스 서비스를 요청한 후 모델 또는 ViewBag을 바인딩할 각 옵션 집합으로 채워야합니다.

다른 방법은 서비스를 보기에 직접 주입하여 옵션을 가져오는 것입니다. 이 방법은 컨트롤러에 필요한 코드 양을 최소화하고, 보기 요소 생성 논리를 보기 자체로 이동합니다. 프로필 편집 양식을 표시하는 컨트롤러 작업은 프로필 인스턴스 양식만 전달하면 됩니다.

using Microsoft.AspNetCore.Mvc;
using ViewInjectSample.Model;

namespace ViewInjectSample.Controllers
{
    public class ProfileController : Controller
    {
        [Route("Profile")]
        public IActionResult Index()
        {
            // TODO: look up profile based on logged-in user
            var profile = new Profile()
            {
                Name = "Steve",
                FavColor = "Blue",
                Gender = "Male",
                State = new State("Ohio","OH")
            };
            return View(profile);
        }
    }
}

이러한 기본 설정을 수정하기 위한 HTML 양식으로는 세 가지 속성에 대한 드롭다운 목록을 포함합니다.

Update Profile view with a form allowing the entry of name, gender, state, and favorite Color.

이러한 목록은 보기에 주입된 서비스에 의해서 채워집니다.

@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는 이 양식에 필요한 데이터만 제공하도록 설계된 UI 수준 서비스입니다.

using System.Collections.Generic;

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

        public List<State> ListStates()
        {
            // a few states from USA
            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" };
        }
    }
}

Important

Startup.ConfigureServices에서 종속성 주입을 통해 요청할 형식을 등록하는 것을 잊지 마세요. 등록되지 않은 형식은 서비스 공급자가 내부적으로 을 통해 GetRequiredService쿼리되기 때문에 런타임에 예외를 throw합니다.

서비스 재정의

새 서비스 주입 외에도, 이 기술은 페이지에서 이전에 주입된 서비스를 재정의하는 데 사용할 수도 있습니다. 아래 그림은 첫 번째 예에서 사용된 페이지에서 사용 가능한 모든 필드를 보여줍니다.

Intellisense contextual menu on a typed @ symbol listing Html, Component, StatsService, and Url fields

볼 수 있는 것처럼 기본 필드에는 Html, ComponentUrl(그리고 우리가 주입한 StatsService)이 포함됩니다. 예를 들어 기본 HTML 도우미를 개발자 고유의 도우미로 바꾸려면 간단하게 @inject를 사용하면 됩니다.

@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>

기존 서비스를 확장하려는 경우 간단하게 이 기술을 사용하여 기존 구현을 상속하거나 기존 구현을 개발자 고유의 구현으로 래핑하면 됩니다.

참고 항목