ASP.NET Core でのビューへの依存関係の挿入

ASP.NET Core では、ビューへの依存関係の挿入をサポートしています。 この機能は、ビュー要素を作成するためだけに必要なローカライズやデータなど、ビュー固有のサービスに役立ちます。 ほとんどのデータ ビューの表示は、コントローラーから渡す必要があります。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

構成の挿入

設定ファイル内の値 (たとえば appsettings.json 、ビュー appsettings.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 から作成されます。 このサービスは、次の中で依存関係の挿入にConfigureServicesProgram.cs登録されます。

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 ビュー。

ルックアップ データの作成

ビューの挿入は、ドロップダウン リストなど、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 フォームには、次の 3 つのプロパティのドロップダウン リストが含まれます。

名前、性別、状態、好きな色の入力を許可するフォームを含む、[Update 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 は、このフォームに必要なデータだけを提供する 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

サービスのオーバーライド

新しいサービスを挿入するだけでなく、この手法を使用して、以前に挿入されたサービスをページでオーバーライドすることもできます。 以下の図は、最初の例で使用されたページで利用できるすべてのフィールドを示しています。

@ 記号を入力して、Html、Component、StatsService、Url フィールドが一覧された IntelliSense コンテキスト メニュー

既定のフィールドには Html、、 Component、および Url. 既定の 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 から作成されます。 このサービスは、次の中で依存関係の挿入にConfigureServicesStartup.cs登録されます。

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 ビュー。

ルックアップ データの作成

ビューの挿入は、ドロップダウン リストなど、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 フォームには、3 つのプロパティのドロップダウン リストが含まれます。

名前、性別、状態、好きな色の入力を許可するフォームを含む、[Update 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 は、このフォームに必要なデータだけを提供する 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" };
        }
    }
}

重要

Startup.ConfigureServices での依存関係の挿入を介して要求する型を登録するのを忘れないでください。 サービス プロバイダーが内部的にクエリを実行するため、登録されていない型は実行時に例外を GetRequiredServiceスローします。

サービスのオーバーライド

新しいサービスを挿入するだけでなく、この技術はページ上の以前に挿入されたサービスをオーバーライドするためにも使用できます。 以下の図は、最初の例で使用されたページで利用できるすべてのフィールドを示しています。

@ 記号を入力して、Html、Component、StatsService、Url フィールドが一覧された IntelliSense コンテキスト メニュー

ご覧のように、既定のフィールドには、HtmlComponentUrl (さらに、挿入した 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>

既存のサービスを拡張する場合、独自に既存の実装から継承したり、ラップしたりするときに、この技術を使用できます。

参照