Partager via


Migrer à partir d’ASP.NET Web Forms vers Blazor

Conseil

Ce contenu est un extrait du livre électronique, Blazor pour les développeurs ASP NET Web Forms pour Azure, disponible dans la documentation .NET ou au format PDF à télécharger gratuitement pour le lire hors connexion.

Blazor-for-ASP-NET-Web-Forms-Developers eBook cover thumbnail.

La migration d’une base de code à partir d’ASP.NET Web Forms vers Blazor est une tâche fastidieuse qui nécessite une planification. Ce chapitre décrit le processus. Une solution qui permet de faciliter la transition consiste à s’assurer que l’application respecte une architecture multiniveau, où le modèle d’application (dans ce cas, Web Forms) est distinct de la logique métier. Cette séparation logique des couches indique clairement ce qui doit être déplacé vers .NET Core et Blazor.

Pour cet exemple, l’application eShop disponible sur GitHub est utilisée. eShop est un service de catalogue qui fournit des capacités CRUD via l’entrée de formulaire et la validation.

Pourquoi une application opérationnelle doit-elle être migrée vers Blazor ? Beaucoup de fois, il n’y a pas besoin. ASP.NET Web Forms continuera d’être pris en charge pendant de nombreuses années. Toutefois, la plupart des fonctionnalités fournies par Blazor sont uniquement prises en charge sur une application migrée. Les fonctionnalités incluent :

  • Améliorations des performances dans l’infrastructure, telles que Span<T>
  • Possibilité de s’exécuter en tant que WebAssembly
  • Prise en charge multiplateforme pour Linux et macOS
  • Déploiement local d’applications ou déploiement de framework partagé sans impact sur d’autres applications

Si ces fonctionnalités ou d’autres nouvelles fonctionnalités sont suffisamment attrayantes, il peut y avoir une valeur dans la migration de l’application. La migration peut prendre différentes formes ; il peut s’agir de l’ensemble de l’application ou de certains points de terminaison qui nécessitent les modifications. La décision de migrer est finalement basée sur les problèmes métier à résoudre par le développeur.

Hébergement côté serveur et côté client

Comme décrit dans le chapitre des modèles d’hébergement, une application Blazor peut être hébergée de deux façons différentes : côté serveur et côté client. Le modèle côté serveur utilise des connexions ASP.NET Core SignalR pour gérer les mises à jour DOM lors de l’exécution de tout code réel sur le serveur. Le modèle côté client s’exécute en tant que WebAssembly dans un navigateur et ne nécessite aucune connexion de serveur. Il existe plusieurs différences qui peuvent affecter ce qui convient le mieux à une application spécifique :

  • L’exécution en tant que WebAssembly ne prend pas en charge toutes les fonctionnalités (telles que le threading) à l’heure actuelle
  • Une communication importante entre le client et le serveur peut entraîner des problèmes de latence en mode côté serveur
  • L’accès aux bases de données et aux services internes ou protégés nécessite un service distinct avec l’hébergement côté client

Au moment de l’écriture, le modèle côté serveur ressemble plus Web Forms. La plupart de ce chapitre se concentre sur le modèle d’hébergement côté serveur, car il est prêt pour la production.

Créer un projet

Cette étape de migration initiale consiste à créer un projet. Ce type de projet est basé sur les projets de style SDK de .NET et simplifie une grande partie de la réutilisable utilisée dans les formats de projet précédents. Pour plus d’informations, consultez le chapitre sur la Structure de projet.

Une fois le projet créé, installez les bibliothèques utilisées dans le projet précédent. Dans les anciens projets Web Forms, vous avez peut-être utilisé le fichier packages.config pour répertorier les packages NuGet requis. Dans le nouveau projet de style SDK, packages.config a été remplacé par des éléments <PackageReference> dans le fichier projet. L’un des avantages de cette approche est que toutes les dépendances sont installées de manière transitive. Vous listez uniquement les dépendances de niveau supérieur qui vous intéressent.

La plupart des dépendances que vous utilisez sont disponibles pour .NET, notamment Entity Framework 6 et log4net. Si aucune version .NET ou .NET Standard n’est disponible, la version du .NET Framework peut souvent être utilisée. Votre kilométrage peut varier. Toute API utilisée qui n’est pas disponible dans .NET provoque une erreur de runtime. Visual Studio vous informe de la présence de tels paquets. Une icône jaune s’affiche sur le nœud Références du projet dans Explorateur de solutions.

Dans le projet eShop basé sur Blazor, vous pouvez voir les packages installés. Auparavant, le fichier packages.config listait chaque package utilisé dans le projet, ce qui entraînait une fichier de près de 50 lignes de long. Un extrait de code de packages.config est :

<?xml version="1.0" encoding="utf-8"?>
<packages>
  ...
  <package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.4.0" targetFramework="net472" />
  <package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.9.1" targetFramework="net472" />
  <package id="Microsoft.ApplicationInsights.PerfCounterCollector" version="2.9.1" targetFramework="net472" />
  <package id="Microsoft.ApplicationInsights.Web" version="2.9.1" targetFramework="net472" />
  <package id="Microsoft.ApplicationInsights.WindowsServer" version="2.9.1" targetFramework="net472" />
  <package id="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel" version="2.9.1" targetFramework="net472" />
  <package id="Microsoft.AspNet.FriendlyUrls" version="1.0.2" targetFramework="net472" />
  <package id="Microsoft.AspNet.FriendlyUrls.Core" version="1.0.2" targetFramework="net472" />
  <package id="Microsoft.AspNet.ScriptManager.MSAjax" version="5.0.0" targetFramework="net472" />
  <package id="Microsoft.AspNet.ScriptManager.WebForms" version="5.0.0" targetFramework="net472" />
  ...
  <package id="System.Memory" version="4.5.1" targetFramework="net472" />
  <package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net472" />
  <package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net472" />
  <package id="System.Threading.Channels" version="4.5.0" targetFramework="net472" />
  <package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net472" />
  <package id="WebGrease" version="1.6.0" targetFramework="net472" />
</packages>

L’élément <packages> inclut toutes les dépendances nécessaires. Il est difficile d’identifier quels packages sont inclus, car vous en avez besoin. Certains éléments <package> sont répertoriés simplement pour répondre aux besoins des dépendances dont vous avez besoin.

Le projet Blazor répertorie les dépendances dont vous avez besoin dans un élément <ItemGroup> dans le fichier projet :

<ItemGroup>
    <PackageReference Include="Autofac" Version="4.9.3" />
    <PackageReference Include="EntityFramework" Version="6.4.4" />
    <PackageReference Include="log4net" Version="2.0.12" />
    <PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="2.2.12" />
</ItemGroup>

Un package NuGet qui simplifie la durée de vie des développeurs Web Forms est le pack de compatibilité Windows. Bien que .NET soit multiplateforme, certaines fonctionnalités sont disponibles uniquement sur Windows. Les fonctionnalités spécifiques à Windows sont disponibles en installant le pack de compatibilité. Parmi ces fonctionnalités, citons le Registre, WMI et les services d’annuaire. Le package ajoute environ 20 000 API et active de nombreux services avec lesquels vous êtes peut-être déjà familiarisé. Le projet eShop ne nécessite pas le pack de compatibilité ; mais si vos projets utilisent des fonctionnalités spécifiques à Windows, le package facilite les efforts de migration.

Activer le processus de démarrage

Le processus de démarrage de Blazor n’est plus Web Forms et il suit une configuration similaire pour d’autres services ASP.NET Core. Quand ils sont hébergés côté serveur, les composants Razor sont exécutés dans le cadre d’une application ASP.NET Core normale. Lorsqu’ils sont hébergés dans le navigateur avec WebAssembly, les composants Razor utilisent un modèle d’hébergement similaire. La différence est que les composants sont exécutés en tant que service distinct de l’un des processus back-end. Dans les deux cas, le démarrage est similaire.

Le fichier Global.asax.cs est la page de démarrage par défaut pour les projets Web Forms. Dans le projet eShop, ce fichier configure le conteneur Inversion of Control (IoC) et gère les différents événements de cycle de vie de l’application ou de la demande. Certains de ces événements sont gérés avec un intergiciel (par exemple Application_BeginRequest). D’autres événements nécessitent la substitution de services spécifiques via l’injection de dépendances (DI).

Par exemple, le fichier Global.asax.cs pour eShop contient le code suivant :

public class Global : HttpApplication, IContainerProviderAccessor
{
    private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    static IContainerProvider _containerProvider;
    IContainer container;

    public IContainerProvider ContainerProvider
    {
        get { return _containerProvider; }
    }

    protected void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on app startup
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        ConfigureContainer();
        ConfigDataBase();
    }

    /// <summary>
    /// Track the machine name and the start time for the session inside the current session
    /// </summary>
    protected void Session_Start(Object sender, EventArgs e)
    {
        HttpContext.Current.Session["MachineName"] = Environment.MachineName;
        HttpContext.Current.Session["SessionStartTime"] = DateTime.Now;
    }

    /// <summary>
    /// https://autofaccn.readthedocs.io/en/latest/integration/webforms.html
    /// </summary>
    private void ConfigureContainer()
    {
        var builder = new ContainerBuilder();
        var mockData = bool.Parse(ConfigurationManager.AppSettings["UseMockData"]);
        builder.RegisterModule(new ApplicationModule(mockData));
        container = builder.Build();
        _containerProvider = new ContainerProvider(container);
    }

    private void ConfigDataBase()
    {
        var mockData = bool.Parse(ConfigurationManager.AppSettings["UseMockData"]);

        if (!mockData)
        {
            Database.SetInitializer<CatalogDBContext>(container.Resolve<CatalogDBInitializer>());
        }
    }

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        //set the property to our new object
        LogicalThreadContext.Properties["activityid"] = new ActivityIdHelper();

        LogicalThreadContext.Properties["requestinfo"] = new WebRequestInfo();

        _log.Debug("Application_BeginRequest");
    }
}

Le fichier précédent devient le fichier Program.cs côté serveur Blazor :

using eShopOnBlazor.Models;
using eShopOnBlazor.Models.Infrastructure;
using eShopOnBlazor.Services;
using log4net;
using System.Data.Entity;
using eShopOnBlazor;

var builder = WebApplication.CreateBuilder(args);

// add services

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

if (builder.Configuration.GetValue<bool>("UseMockData"))
{
    builder.Services.AddSingleton<ICatalogService, CatalogServiceMock>();
}
else
{
    builder.Services.AddScoped<ICatalogService, CatalogService>();
    builder.Services.AddScoped<IDatabaseInitializer<CatalogDBContext>, CatalogDBInitializer>();
    builder.Services.AddSingleton<CatalogItemHiLoGenerator>();
    builder.Services.AddScoped(_ => new CatalogDBContext(builder.Configuration.GetConnectionString("CatalogDBContext")));
}

var app = builder.Build();

new LoggerFactory().AddLog4Net("log4Net.xml");

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
}

// Middleware for Application_BeginRequest
app.Use((ctx, next) =>
{
    LogicalThreadContext.Properties["activityid"] = new ActivityIdHelper(ctx);
    LogicalThreadContext.Properties["requestinfo"] = new WebRequestInfo(ctx);
    return next();
});

app.UseStaticFiles();

app.UseRouting();

app.UseEndpoints(endpoints =>
{
    endpoints.MapBlazorHub();
    endpoints.MapFallbackToPage("/_Host");
});

ConfigDataBase(app);

void ConfigDataBase(IApplicationBuilder app)
{
    using (var scope = app.ApplicationServices.CreateScope())
    {
        var initializer = scope.ServiceProvider.GetService<IDatabaseInitializer<CatalogDBContext>>();

        if (initializer != null)
        {
            Database.SetInitializer(initializer);
        }
    }
}

app.Run();

Un changement important que vous remarquerez peut-être de Web Forms est l’importance de l’injection de dépendances (DI). DI a été un principe directeur dans la conception d’ASP.NET Core. Elle prend en charge la personnalisation de presque tous les aspects de l’infrastructure ASP.NET Core. Il existe même un fournisseur de services intégré qui peut être utilisé pour de nombreux scénarios. Si d’autres personnalisations sont requises, elles peuvent être prises en charge par de nombreux projets communautaires. Par exemple, vous pouvez transférer votre investissement de bibliothèque de DI tiers.

Dans l’application eShop d’origine, il existe une configuration pour la gestion des sessions. Étant donné que Blazor côté serveur utilise ASP.NET Core SignalR pour la communication, l’état de session n’est pas pris en charge, car les connexions peuvent se produire indépendamment d’un contexte HTTP. Une application qui utilise l’état de session nécessite une nouvelle architecture avant de l’exécuter en tant qu’application Blazor.

Pour plus d’informations sur le démarrage de l’application, consultez Démarrage de l’application.

Migrer des modules et gestionnaires HTTP vers des intergiciels

Les modules et les gestionnaires HTTP sont des modèles courants dans Web Forms pour contrôler le pipeline de requête HTTP. Des classes qui implémentent IHttpModule ou IHttpHandler peuvent être inscrites et traiter les demandes entrantes. Web Forms configure des modules et des gestionnaires dans le fichier web.config. Web Forms est également fortement basé sur la gestion des événements du cycle de vie des applications. ASP.NET Core utilise plutôt l’intergiciel. L’intergiciel est inscrit dans la méthode Configure de la classe Startup. L’ordre d’exécution des intergiciels est déterminé par l’ordre d’enregistrement.

Dans la section Activer le processus de démarrage, un événement de cycle de vie a été déclenché par Web Forms comme la méthode Application_BeginRequest. Cet événement n’est pas disponible dans ASP.NET Core. Pour atteindre ce comportement, vous pouvez implémenter un intergiciel comme indiqué dans l’exemple de fichier Startup.cs. Cet intergiciel effectue la même logique, puis transfère le contrôle au gestionnaire suivant dans le pipeline d’intergiciels.

Pour plus d’informations sur la migration de modules et de gestionnaires, consultez Migrer des gestionnaires et des modules HTTP vers un intergiciel ASP.NET Core.

Migrer des fichiers statiques

Pour traiter des fichiers statiques (par exemple, HTML, CSS, images et JavaScript), les fichiers doivent être exposés par intergiciel. L’appel de la méthode UseStaticFiles active le service de fichiers statiques à partir du chemin racine du web. Le répertoire racine web par défaut est wwwroot, mais il peut être personnalisé. Comme inclus dans le fichier Program.cs :

...

app.UseStaticFiles();

...

Le projet eShop permet d’accéder aux fichiers statiques de base. De nombreuses personnalisations sont disponibles pour l’accès aux fichiers statiques. Pour plus d’informations sur l’activation des fichiers par défaut ou d’un navigateur de fichiers, consultez Fichiers statiques dans ASP.NET Core.

Migration du regroupement de runtime et de la configuration de la minimisation

Le regroupement et la minimisation sont des techniques d’optimisation des performances permettant de réduire le nombre et la taille des demandes de serveur pour récupérer certains types de fichiers. JavaScript et CSS subissent souvent une forme de regroupement ou de minimisation avant d’être envoyés au client. Dans ASP.NET Web Forms, ces optimisations sont gérées au moment de l’exécution. Les conventions d’optimisation sont définies dans un fichier App_Start/BundleConfig.cs. Dans ASP.NET Core, une approche plus déclarative est adoptée. Un fichier répertorie les fichiers à réduire, ainsi que les paramètres de minimisation spécifiques.

Pour plus d’informations sur le regroupement et la minimisation, consultez Regrouper et minimiser les ressources statiques dans ASP.NET Core.

Migrer des pages ASPX

Une page d’une application Web Forms est un fichier avec l’extension .aspx. Une page Web Forms peut souvent être mappée à un composant dans Blazor. Un composant Razor est créé dans un fichier avec l’extension .razor. Pour le projet eShop, cinq pages sont converties en page Razor.

Par exemple, la vue détails comprend trois fichiers dans le projet Web Forms : Details.aspx, Details.aspx.cs et Details.aspx.designer.cs. Lors de la conversion en Blazor, le code-behind et le balisage sont combinés en Details.razor. La compilation Razor (équivalente à ce qui se trouve dans les fichiers .designer.cs) est stockée dans le répertoire obj et n’est pas, par défaut, visible dans Explorateur de solutions. La page Web Forms se compose du balisage suivant :

<%@ Page Title="Details" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Details.aspx.cs" Inherits="eShopLegacyWebForms.Catalog.Details" %>

<asp:Content ID="Details" ContentPlaceHolderID="MainContent" runat="server">
    <h2 class="esh-body-title">Details</h2>

    <div class="container">
        <div class="row">
            <asp:Image runat="server" CssClass="col-md-6 esh-picture" ImageUrl='<%#"/Pics/" + product.PictureFileName%>' />
            <dl class="col-md-6 dl-horizontal">
                <dt>Name
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.Name%>' />
                </dd>

                <dt>Description
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.Description%>' />
                </dd>

                <dt>Brand
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.CatalogBrand.Brand%>' />
                </dd>

                <dt>Type
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.CatalogType.Type%>' />
                </dd>
                <dt>Price
                </dt>

                <dd>
                    <asp:Label CssClass="esh-price" runat="server" Text='<%#product.Price%>' />
                </dd>

                <dt>Picture name
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.PictureFileName%>' />
                </dd>

                <dt>Stock
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.AvailableStock%>' />
                </dd>

                <dt>Restock
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.RestockThreshold%>' />
                </dd>

                <dt>Max stock
                </dt>

                <dd>
                    <asp:Label runat="server" Text='<%#product.MaxStockThreshold%>' />
                </dd>

            </dl>
        </div>

        <div class="form-actions no-color esh-link-list">
            <a runat="server" href='<%# GetRouteUrl("EditProductRoute", new {id =product.Id}) %>' class="esh-link-item">Edit
            </a>
            |
            <a runat="server" href="~" class="esh-link-item">Back to list
            </a>
        </div>

    </div>
</asp:Content>

Le code-behind du balisage précédent inclut le code suivant :

using eShopLegacyWebForms.Models;
using eShopLegacyWebForms.Services;
using log4net;
using System;
using System.Web.UI;

namespace eShopLegacyWebForms.Catalog
{
    public partial class Details : System.Web.UI.Page
    {
        private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        protected CatalogItem product;

        public ICatalogService CatalogService { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            var productId = Convert.ToInt32(Page.RouteData.Values["id"]);
            _log.Info($"Now loading... /Catalog/Details.aspx?id={productId}");
            product = CatalogService.FindCatalogItem(productId);

            this.DataBind();
        }
    }
}

Lorsqu’elle est convertie en Blazor, la page Web Forms se traduit par le code suivant :

@page "/Catalog/Details/{id:int}"
@inject ICatalogService CatalogService
@inject ILogger<Details> Logger

<h2 class="esh-body-title">Details</h2>

<div class="container">
    <div class="row">
        <img class="col-md-6 esh-picture" src="@($"/Pics/{_item.PictureFileName}")">

        <dl class="col-md-6 dl-horizontal">
            <dt>
                Name
            </dt>

            <dd>
                @_item.Name
            </dd>

            <dt>
                Description
            </dt>

            <dd>
                @_item.Description
            </dd>

            <dt>
                Brand
            </dt>

            <dd>
                @_item.CatalogBrand.Brand
            </dd>

            <dt>
                Type
            </dt>

            <dd>
                @_item.CatalogType.Type
            </dd>
            <dt>
                Price
            </dt>

            <dd>
                @_item.Price
            </dd>

            <dt>
                Picture name
            </dt>

            <dd>
                @_item.PictureFileName
            </dd>

            <dt>
                Stock
            </dt>

            <dd>
                @_item.AvailableStock
            </dd>

            <dt>
                Restock
            </dt>

            <dd>
                @_item.RestockThreshold
            </dd>

            <dt>
                Max stock
            </dt>

            <dd>
                @_item.MaxStockThreshold
            </dd>

        </dl>
    </div>

    <div class="form-actions no-color esh-link-list">
        <a href="@($"/Catalog/Edit/{_item.Id}")" class="esh-link-item">
            Edit
        </a>
        |
        <a href="/" class="esh-link-item">
            Back to list
        </a>
    </div>

</div>

@code {
    private CatalogItem _item;

    [Parameter]
    public int Id { get; set; }

    protected override void OnInitialized()
    {
        Logger.LogInformation("Now loading... /Catalog/Details/{Id}", Id);

        _item = CatalogService.FindCatalogItem(Id);
    }
}

Notez que le code et le balisage se trouvent dans le même fichier. Tous les services requis sont rendus accessibles avec l’attribut @inject. Conformément à la directive @page, cette page est accessible à l’itinéraire Catalog/Details/{id}. La valeur de l’espace réservé {id} de l’itinéraire a été limitée à un entier. Comme décrit dans la section routage, contrairement à Web Forms, un composant Razor indique explicitement son itinéraire et tous les paramètres inclus. De nombreux contrôles Web Forms peuvent ne pas avoir d’équivalents exacts dans Blazor. Il existe souvent un extrait de code HTML équivalent qui servira le même objectif. Par exemple, le contrôle <asp:Label /> peut être remplacé par un élément <label> HTML.

Validation de modèle dans Blazor

Si votre code Web Forms inclut la validation, vous pouvez transférer une grande partie de ce que vous avez avec peu ou aucun changement. L’un des avantages de l’exécution dans Blazor est que la même logique de validation peut être exécutée sans avoir besoin de JavaScript personnalisé. Les annotations de données permettent une validation de modèle facile.

Par exemple, la page Create.aspx comporte un formulaire d’entrée de données avec validation. Voici un exemple d’extrait de code :

<div class="form-group">
    <label class="control-label col-md-2">Name</label>
    <div class="col-md-3">
        <asp:TextBox ID="Name" runat="server" CssClass="form-control"></asp:TextBox>
        <asp:RequiredFieldValidator runat="server" ControlToValidate="Name" Display="Dynamic"
            CssClass="field-validation-valid text-danger" ErrorMessage="The Name field is required." />
    </div>
</div>

Dans Blazor, le balisage équivalent est fourni dans un fichier Create.razor :

<EditForm Model="_item" OnValidSubmit="@...">
    <DataAnnotationsValidator />

    <div class="form-group">
        <label class="control-label col-md-2">Name</label>
        <div class="col-md-3">
            <InputText class="form-control" @bind-Value="_item.Name" />
            <ValidationMessage For="(() => _item.Name)" />
        </div>
    </div>

    ...
</EditForm>

Le contexte EditForm inclut la prise en charge de la validation et peut être encapsulé autour d’une entrée. Les annotations de données sont un moyen courant d’ajouter une validation. Cette prise en charge de validation peut être ajoutée via le composant DataAnnotationsValidator. Pour plus d’informations sur ce mécanisme, consultez Formulaires Blazor et validation ASP.NET Core.

Migrer une configuration

Dans un projet Web Forms, les données de configuration sont généralement stockées dans le fichier web.config. Les données de configuration sont accessibles avec ConfigurationManager. Les services étaient souvent nécessaires pour analyser des objets. Avec .NET Framework 4.7.2, la composabilité a été ajoutée à la configuration via ConfigurationBuilders. Ces générateurs ont permis aux développeurs d’ajouter différentes sources pour la configuration qui a ensuite été composée au moment de l’exécution pour récupérer les valeurs nécessaires.

ASP.NET Core a introduit un système de configuration flexible qui vous permet de définir la ou les sources de configuration utilisées par votre application et votre déploiement. L’infrastructure ConfigurationBuilder que vous utilisez peut-être dans votre application Web Forms a été modélisée d’après les concepts utilisés dans le système de configuration ASP.NET Core.

L’extrait de code suivant montre comment le projet eShop Web Forms utilise web.config pour stocker les valeurs de configuration :

<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="CatalogDBContext" connectionString="Data Source=(localdb)\MSSQLLocalDB; Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb; Integrated Security=True; MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <appSettings>
    <add key="UseMockData" value="true" />
    <add key="UseCustomizationData" value="false" />
  </appSettings>
</configuration>

Il est courant que des secrets, tels que les chaînes de connexion de base de données, soient stockés dans web.config. Les secrets sont inévitablement conservés dans des emplacements non sécurisés, tels que le contrôle de code source. Avec Blazor sur ASP.NET Core, la configuration XML précédente est remplacée par le code JSON suivant :

{
  "ConnectionStrings": {
    "CatalogDBContext": "Data Source=(localdb)\\MSSQLLocalDB; Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb; Integrated Security=True; MultipleActiveResultSets=True;"
  },
  "UseMockData": true,
  "UseCustomizationData": false
}

JSON est le format de configuration par défaut. Toutefois, ASP.NET Core prend en charge de nombreux autres formats, notamment XML. Il existe également plusieurs formats pris en charge par la communauté.

Vous pouvez accéder aux valeurs de configuration à partir du générateur dans Program.cs :

if (builder.Configuration.GetValue<bool>("UseMockData"))
{
    builder.Services.AddSingleton<ICatalogService, CatalogServiceMock>();
}
else
{
    builder.Services.AddScoped<ICatalogService, CatalogService>();
    builder.Services.AddScoped<IDatabaseInitializer<CatalogDBContext>, CatalogDBInitializer>();
    builder.Services.AddSingleton<CatalogItemHiLoGenerator>();
    builder.Services.AddScoped(_ => new CatalogDBContext(builder.Configuration.GetConnectionString("CatalogDBContext")));
}

Par défaut, les variables d’environnement, les fichiers JSON (appsettings.json et appsettings.{ Environment}.json) et les options de ligne de commande sont inscrits en tant que sources de configuration valides dans l’objet de configuration. Les sources de configuration sont accessibles via Configuration[key]. Une technique plus avancée consiste à lier les données de configuration aux objets à l’aide du modèle d’options. Pour plus d’informations sur la configuration et le modèle d’options, consultez Configuration dans ASP.NET Core et Modèle Options dans ASP.NET Core, respectivement.

Migrer l’accès aux données

L’accès aux données est un aspect important de n’importe quelle application. Le projet eShop stocke les informations de catalogue dans une base de données et récupère les données avec Entity Framework (EF) 6. Étant donné qu’EF 6 est pris en charge dans .NET 5, le projet peut continuer à l’utiliser.

Les modifications liées à EF suivantes ont été nécessaires pour eShop :

  • Dans .NET Framework, l’objet DbContext accepte une chaîne du formulaire name=ConnectionString et utilise la chaîne de connexion à partir de ConfigurationManager.AppSettings[ConnectionString] pour se connecter. Dans .NET Core, cela n’est pas pris en charge. La chaîne de connexion doit être fournie.
  • L'accès à la base de données s'est fait de manière synchrone. Bien que cela fonctionne, la scalabilité peut souffrir. Cette logique doit être déplacée vers un modèle asynchrone.

Bien qu’il n’y ait pas la même prise en charge native de la liaison de jeu de données, Blazor offre une flexibilité et une puissance avec sa prise en charge C# dans une page Razor. Par exemple, vous pouvez effectuer des calculs et afficher le résultat. Pour plus d’informations sur les modèles de données dans Blazor, consultez le chapitre Accès aux données.

Modifications architecturales

Enfin, il existe quelques différences architecturales importantes à prendre en compte lors de la migration vers Blazor. La plupart de ces modifications s’appliquent à tout ce qui est basé sur .NET Core ou ASP.NET Core.

Étant donné que Blazor s’appuie sur .NET Core, il existe des considérations à prendre en compte pour garantir la prise en charge sur .NET Core. Parmi les principales modifications, citons la suppression des fonctionnalités suivantes :

  • Plusieurs AppDomains
  • Communication à distance
  • Sécurité d'accès du code
  • Transparence de la sécurité

Pour plus d’informations sur les techniques permettant d’identifier les modifications nécessaires pour prendre en charge l’exécution sur .NET Core, consultez Amener votre code de .NET Framework vers .NET Core.

ASP.NET Core est une version réimaginée de ASP.NET et a des modifications qui peuvent ne pas sembler évidentes initialement. Les principales modifications sont les suivantes :

  • Aucun contexte de synchronisation, ce qui signifie qu’il n’y a pas HttpContext.Current, Thread.CurrentPrincipal, ni d’autres accesseurs statiques
  • Pas de cliché instantané
  • Pas de file d'attente de requêtes

De nombreuses opérations dans ASP.NET Core sont asynchrones, ce qui facilite le déchargement des tâches liées aux E/S. Il est important de ne jamais bloquer à l’aide de Task.Wait() ou Task.GetResult(), qui peuvent rapidement épuiser les ressources du pool de threads.

Conclusion de la migration

À ce stade, vous avez vu de nombreux exemples de ce qu’il faut faire pour déplacer un projet Web Forms vers Blazor. Pour obtenir un exemple complet, consultez le projet eShopOnBlazor.