Injection de dépendances dans les vues dans ASP.NET Core

ASP.NET Core prend en charge l’injection de dépendances dans les vues. Cette fonctionnalité peut être utile pour les services spécifiques à une vue, notamment la localisation ou les données requises uniquement pour remplir les éléments de la vue. La plupart des affichages de vues de données doivent être passés par le contrôleur.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Injection de configuration

Les valeurs dans les fichiers de paramètres, comme appsettings.json et appsettings.Development.json, peuvent être injectées dans une vue. Considérez le appsettings.Development.json à partir de l’exemple de code :

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

Le balisage suivant affiche la valeur de la configuration dans un mode Page 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>

Le balisage suivant affiche la valeur de la configuration dans une vue 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>

Pour plus d’informations, consultez Configuration dans ASP.NET Core

Injection de service

Un service peut être injecté dans une vue en utilisant la directive @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>

Cette vue affiche une liste d’instances ToDoItem et un récapitulatif de statistiques générales. Le récapitulatif est rempli avec les données du service StatisticsService injecté. Ce service est inscrit pour l’injection de dépendances sous ConfigureServices dans Program.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 effectue des calculs sur l’ensemble des instances ToDoItem, auquel il accède par le biais d’un référentiel :

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);
        }
    }
}

Le référentiel de l’exemple utilise une collection en mémoire. Une implémentation en mémoire ne doit pas être utilisée pour les jeux de données volumineux et accessibles à distance.

L’exemple affiche les données fournies par le modèle lié à la vue et le service injecté dans la vue :

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

Remplissage des données de recherche

L’injection dans les vues peut être utile pour remplir certaines options dans les éléments d’interface utilisateur, telles que les listes déroulantes. Prenons l’exemple d’un formulaire de profil utilisateur qui comporte des options permettant de spécifier le sexe, l’État et d’autres préférences. Le rendu d’un tel formulaire avec une approche standard peut nécessiter le contrôleur ou la page Razor pour :

  • Demander des services d’accès aux données pour chaque ensemble d’options.
  • Remplissez un modèle ou ViewBag avec chacun des ensembles d’options à lier.

Une autre approche consiste à injecter les services directement dans la vue pour obtenir les options. Cela réduit la quantité de code requis par le contrôleur ou la page razor, car la logique de construction de cet élément de vue est déplacée dans la vue proprement dite. Pour afficher un formulaire de modification de profil, il suffit ainsi au contrôleur ou à la page Razor de passer l’instance de profil au formulaire :

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);
    }
}

Le formulaire HTML utilisé pour mettre à jour les préférences contient des listes déroulantes pour trois des propriétés :

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

Ces listes sont remplies par un service qui a été injecté dans la vue :

@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 est un service au niveau de l’interface utilisateur qui est conçu pour fournir uniquement les données nécessaires dans ce formulaire :

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" };
    }
}

Un type non inscrit lève une exception au moment de l’exécution, car le fournisseur de services est interrogé en interne à travers GetRequiredService.

Substitution de services

En plus d’être une technique pouvant servir à injecter de nouveaux services, l’injection de service permet de substituer des services ayant déjà été injectés dans une page. La capture d’écran ci-dessous montre tous les champs disponibles dans la page utilisée dans le premier exemple :

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

Les champs par défaut sont Html, Component et Url. Pour remplacer les assistants HTML par défaut par une version personnalisée, utilisez @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>

Voir aussi

ASP.NET Core prend en charge l’injection de dépendances dans les vues. Cette fonctionnalité peut être utile pour les services spécifiques à une vue, notamment la localisation ou les données requises uniquement pour remplir les éléments de la vue. Vous devez essayer de respecter le principe de séparation des préoccupations entre les contrôleurs et les vues. La plupart des données affichées dans vos vues doivent être passées par le contrôleur.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Injection de configuration

Les valeurs appsettings.json peuvent être injectées directement dans une vue.

Exemple d’un fichier appsettings.json :

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

Syntaxe de la directive @inject : @inject <type> <name>

Exemple avec @inject :

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

Injection de service

Un service peut être injecté dans une vue en utilisant la directive @inject. @inject équivaut à ajouter une propriété à la vue et à remplir la propriété à l’aide de l’injection de dépendances.

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

Cette vue affiche une liste d’instances ToDoItem et un récapitulatif de statistiques générales. Le récapitulatif est rempli avec les données du service StatisticsService injecté. Ce service est inscrit pour l’injection de dépendances sous ConfigureServices dans Startup.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 effectue des calculs sur l’ensemble des instances ToDoItem, auquel il accède par le biais d’un référentiel :

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);
        }
    }
}

Le référentiel de l’exemple utilise une collection en mémoire. L’implémentation illustrée ci-dessus (qui s’applique à toutes les données en mémoire) n’est pas recommandée pour les jeux de données volumineux et accessibles à distance.

L’exemple affiche les données fournies par le modèle lié à la vue et le service injecté dans la vue :

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

Remplissage des données de recherche

L’injection dans les vues peut être utile pour remplir certaines options dans les éléments d’interface utilisateur, telles que les listes déroulantes. Prenons l’exemple d’un formulaire de profil utilisateur qui comporte des options permettant de spécifier le sexe, l’État et d’autres préférences. Pour afficher un formulaire de ce type selon une approche MVC standard, il faudrait que le contrôleur demande l’accès des données aux services pour chacune des options du formulaire, puis qu’il remplisse un modèle ou ViewBag avec chaque ensemble d’options à lier.

Une autre approche consiste à injecter les services directement dans la vue pour obtenir les options. Cela réduit la quantité de code requis par le contrôleur, car la logique de construction de cet élément de vue est déplacée dans la vue proprement dite. Pour afficher un formulaire de modification de profil, il suffit ainsi au contrôleur de passer l’instance de profil au formulaire :

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);
        }
    }
}

Le formulaire HTML utilisé pour mettre à jour ces préférences inclut des listes déroulantes pour trois des propriétés :

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

Ces listes sont remplies par un service qui a été injecté dans la vue :

@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 est un service au niveau de l’interface utilisateur qui est conçu pour fournir uniquement les données nécessaires dans ce formulaire :

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

N’oubliez pas d’inscrire les types à demander par le biais de l’injection de dépendances dans Startup.ConfigureServices. Un type non inscrit lève une exception au moment de l’exécution, car le fournisseur de services est interrogé en interne à travers GetRequiredService.

Substitution de services

L’injection de services peut être utilisée pour injecter de nouveaux services, mais également pour substituer des services ayant déjà été injectés dans une page. La capture d’écran ci-dessous montre tous les champs disponibles dans la page utilisée dans le premier exemple :

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

Comme vous pouvez le voir, les champs par défaut incluent Html, Component et Url (mais aussi StatsService que nous avons injecté). Si vous souhaitez, par exemple, remplacer les HTML Helpers par défaut par les vôtres, vous pouvez facilement le faire en utilisant @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>

Si vous souhaitez étendre des services existants, vous pouvez simplement utiliser cette technique en héritant de ou en incluant dans un wrapper l’implémentation existante avec la vôtre.

Voir aussi