Ce message est né de notre expérience de la migration d'un projet existant d'ASP.NET MVC vers ASP.NET Core. Nous avons essayé de rassembler l'ensemble du processus de migration sous une forme structurée et de décrire les divers goulots d'étranglement afin que les développeurs puissent continuer à s'appuyer sur ce matériel et suivre la feuille de route pour résoudre ces problèmes.
Quelques mots sur notre projet. Nous sommes une plate-forme de commerce électronique open source sur ASP.NET, qui, au moment du transfert, existait déjà avec succès depuis 9 ans. Nous avons fait la migration il y a 2 ans - mais nos mains ne sont venues l'écrire que maintenant. À cette époque, nous étions l'un des premiers grands projets à avoir décidé d'une telle démarche.
Pourquoi passer Ă ASP.NET Core?
Avant de passer aux étapes de migration d'ASP.NET MVC vers ASP.NET Core, quelques mots sur les avantages de cette plateforme.

Avantages d'ASP.NET CoreAinsi, ASP.NET Core est déjà un framework assez bien connu et développé, qui a déjà subi plusieurs mises à jour majeures, ce qui signifie qu'il est aujourd'hui assez stable, technologiquement avancé et résistant aux attaques XSRF / CSRF.
La multiplateforme est l'une des caractéristiques qui lui permettent de gagner de plus en plus en popularité. À partir de maintenant, votre application Web peut fonctionner dans les environnements Windows et Unix.
Modularité - ASP.NET Core se présente entièrement sous la forme de packages NuGet, cela vous permet d'optimiser l'application, y compris les packages nécessaires sélectionnés. Cela améliore les performances de la solution et réduit le temps nécessaire à la mise à niveau des pièces individuelles. Il s'agit de la deuxième fonctionnalité importante qui permet aux développeurs d'intégrer de manière plus flexible de nouvelles fonctionnalités dans leur solution.
Les performances sont une autre étape vers la création d'une application hautes performances, ASP.NET Core traite 2300% de requêtes en plus par seconde qu'ASP.NET 4.6, et 800% de requêtes en plus par seconde que node.js. Vous pouvez examiner vous-même les tests de performances détaillés
ici ou
ici .
Le middleware est le nouveau pipeline modulaire léger et hautes performances pour les requêtes in-app. Chaque élément de middleware traite une demande HTTP, puis décide de renvoyer le résultat ou transmet le morceau de middleware suivant. Cette approche donne au développeur un contrôle total sur le pipeline HTTP et contribue au développement de modules simples pour l'application, ce qui est important pour un projet open source en pleine croissance.
ASP.NET Core MVC fournit des fonctionnalités qui simplifient le développement Web. NopCommerce utilisait déjà des fonctionnalités telles que le modèle Model-View-Controller, la syntaxe Razor, la liaison et la validation de modèle, mais de nouveaux outils sont apparus:
- Tag Helpers. Il s'agit d'un code côté serveur pour contribuer à la création et au rendu des éléments HTML dans les fichiers Razor.
- Afficher les composants. Il s'agit d'un nouvel outil, similaire aux vues partielles, mais beaucoup plus puissant. nopCommerce utilise des composants de vue lorsque la réutilisation de la logique de rendu est requise et lorsque la tâche est trop complexe pour une vue partielle.
- Le point de vue de DI. Bien que la plupart des données affichées dans les vues proviennent du contrôleur, nopCommerce a des vues dans lesquelles l'injection de dépendance est plus pratique.
Bien sûr, ASP.NET Core a beaucoup plus de fonctionnalités, mais nous venons d'examiner les plus intéressantes.
Parlons maintenant de ce que vous devez considérer lors du portage de votre application vers une nouvelle plateforme.
La migration
Le texte contiendra un grand nombre de liens vers la documentation officielle d'ASP.NET Core pour vous aider à obtenir des informations plus détaillées sur le sujet. Particulièrement pertinent pour les développeurs confrontés à une tâche similaire pour la première fois.
Étape 1. Préparation des outils
La première chose que vous devez faire est de mettre à niveau Visual Studio 2017 vers la version 15.3 ou supérieure. Et installez la dernière version du SDK .NET Core.
Avant de commencer la migration, il est recommandé d'utiliser l'outil d'analyse de portabilité .NET
.Net Portability Analyzer . C'est un bon point de départ pour comprendre à quel point la transition d'une plate-forme à une autre sera laborieuse. Mais, bien sûr, cet outil ne résout pas tous les problèmes et, dans le processus, il y aura de nombreux pièges. Ensuite, les principales étapes qui devront être franchies seront décrites et les solutions utilisées dans notre projet seront présentées.
La toute première chose dont vous avez besoin est de mettre à jour les liens vers les bibliothèques utilisées dans le projet qui prendraient en charge .NET Standard.
Étape 2. Analyse de compatibilité des packages NuGet pour prendre en charge la norme .Net
Si vous utilisez des packages NuGet dans votre projet, vous devez vérifier s'ils sont compatibles avec .NET Core. Pour cela, vous pouvez utiliser l'outil
NuGetPackageExplorer .
Étape 3. .NET Core utilise le nouveau format de fichier csproj
Il est important d'utiliser la nouvelle approche pour ajouter des liens tiers introduits dans .NET Core: lorsqu'une nouvelle bibliothèque de classes est ajoutée à la solution, vous devez ouvrir le fichier de projet principal et remplacer son contenu par ce qui suit:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp2.2</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.6" /> ... </ItemGroup> ... </Project>
Les liens des bibliothèques connectées seront téléchargés automatiquement. Plus d'informations sur le mappage entre les propriétés project.json et CSPROJ peuvent être trouvées dans la documentation officielle
ici et
ici .
Étape 4. Mise à jour de l'espace de noms
Vous devez supprimer toutes les utilisations de System.Web et les remplacer par Microsoft.AspNetCore.
Étape 5. Vous devez configurer le fichier Startup.cs. au lieu d'utiliser global.asax
ASP.NET Core dispose d'un nouveau mécanisme de chargement de l'application. Le point d'entrée de l'application devient
Startup
et la dépendance au fichier
Global.asax disparaît.
Startup
enregistre la suite middleware dans l'application.
Startup
doit inclure la méthode
Configure
. Dans
Configure
ajoutez le middleware requis au pipeline.
Problèmes Startup.cs- Configuration du middleware pour les requêtes MVC et WebAPI
- Paramètres de configuration pour:
app.UseMvc(routes => { routes.MapRoute("areaRoute", "{area:exists}/{controller=Admin}/{action=Index}/{id?}"); routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
Dans le même temps, le dossier portant le nom Area, dans lequel se trouve le dossier Admin, doit être placé à la racine de l'application. Désormais, l'attribut
[Area("Admin")] [Route("admin")]
sera utilisé pour connecter le contrôleur à cette zone.
Il ne reste plus qu'à créer des vues pour toutes les actions décrites dans le contrôleur.
[Area("Admin")] [Route("admin")] public class AdminController : Controller { public IActionResult Index() { return View(); } }
ValidationMaintenant, vous n'avez pas besoin de transmettre IFormCollection aux contrôleurs, car dans ce cas, la validation du serveur asp.net est désactivée - MVC supprime toute validation supplémentaire si IFormCollection n'est pas nul. La solution au problème peut être d'ajouter cette propriété au modèle, cela nous évitera de passer directement à la méthode du contrôleur. Cette règle n'est valide que si un modèle est présent, mais s'il n'y a pas de modèle, alors il n'y aura pas de validation.
Les propriétés enfants ne sont plus automatiquement validées. Il doit être spécifié manuellement.
Étape 6. Transfert des gestionnaires HTTP et des modules HTTP vers le middleware
Les gestionnaires HTTP et les modules HTTP sont essentiellement très similaires au concept de
middleware dans ASP.NET Core , mais contrairement aux modules, l'ordre des middlewares est basé sur l'ordre dans lequel ils sont insérés dans le pipeline de demande. L'ordre des modules, pour la plupart, est basé sur
les événements du
cycle de vie de l'application . L'ordre du middleware pour les réponses est l'inverse de l'ordre des requêtes, et l'ordre des modules pour les requêtes et les réponses est le même. Sur cette base, vous pouvez procéder à la mise à niveau.
Alors, ce qui reste Ă mettre Ă jour:
- Migration de modules pour Middleware (AuthenticationMiddleware, CultureMiddleware, etc.)
- Gestionnaires du middleware
- Utilisation d'un nouveau middleware
L'authentification dans notre projet n'utilise pas le système d'informations d'identification intégré; à ces fins, le middleware AuthenticationMiddleware est utilisé, développé conformément à la nouvelle structure ASP.NET Core.
public class AuthenticationMiddleware { private readonly RequestDelegate _next; public AuthenticationMiddleware(IAuthenticationSchemeProvider schemes, RequestDelegate next) { Schemes = schemes ?? throw new ArgumentNullException(nameof(schemes)); _next = next ?? throw new ArgumentNullException(nameof(next)); } public IAuthenticationSchemeProvider Schemes { get; set; } public async Task Invoke(HttpContext context) { context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature { OriginalPath = context.Request.Path, OriginalPathBase = context.Request.PathBase }); var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>(); foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync()) { try { if (await handlers.GetHandlerAsync(context, scheme.Name) is IAuthenticationRequestHandler handler && await handler.HandleRequestAsync()) return; } catch {
ASP.NET fournit de nombreux middleware intégrés que vous pouvez utiliser dans votre application, mais notez que le développeur a la possibilité
de créer son propre middleware et de l'ajouter au pipeline de requêtes HTTP. Pour simplifier ce mécanisme, nous avons ajouté une interface spéciale, et maintenant il suffit de créer une classe qui l'implémente.
public interface INopStartup {
Ici, vous pouvez ajouter et configurer votre middleware:
Étape 7. Utilisation de DI intégré
L' injection de
dépendances est l'une des fonctionnalités clés du processus de conception d'application dans ASP.NET Core. Il vous permet de créer des applications à couplage lâche qui sont plus testables, modulaires et, par conséquent, maintenables. Cela a été rendu possible en suivant le principe de l'inversion de dépendance. Pour installer les dépendances, des conteneurs IoC (Inversion of Control) sont utilisés. Dans ASP.NET Core, un tel conteneur est représenté par l'interface IServiceProvider. Les services sont installés dans l'application dans la méthode
Startup.ConfigureServices()
.
Tout service enregistré peut être configuré avec trois étendues:
- transitoire
- portée
- singleton
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddSingleton<Isingleton,MySingleton>();
Étape 8. Utilisation de shells de compatibilité de projet WebAPI (Shim)
Pour simplifier la migration d'une implémentation d'API Web existante, il est recommandé d'utiliser le package NuGet
Microsoft.AspNetCore.Mvc.WebApiCompatShim . Les
fonctions compatibles suivantes sont prises en charge:
- Ajoute le type ApiController
- Active la liaison de modèle de style API Web
- Étend la liaison de modèle afin que les actions du contrôleur puissent accepter des paramètres de type HttpRequestMessage.
- Ajoute des formateurs de messages qui permettent aux actions de renvoyer des résultats de type HttpResponseMessage
services.AddMvc().AddWebApiConventions(); routes.MapWebApiRoute(name: "DefaultApi", template: "api/{controller}/{id?}" );
Étape 9. Transfert de la configuration de l'application
Auparavant, certains paramètres étaient enregistrés dans le fichier web.config. Nous adoptons maintenant une
nouvelle approche basée sur des paires clé-valeur établies
par les fournisseurs de configuration . Il s'agit du mécanisme recommandé dans ASP.NET Core et nous utilisons le fichier appsettings.json.
Vous pouvez également utiliser le package NuGet
System.Configuration.ConfigurationManager
si, pour une raison quelconque, vous souhaitez continuer à utiliser * .config. Dans ce cas, vous devrez abandonner la possibilité d'exécuter l'application sur les plates-formes Unix et de l'exécuter uniquement sous IIS.
Si vous souhaitez utiliser le fournisseur de configuration
Azure Key Vault , vous devez vous référer au contenu Content
Migration to Azure Key Valut . Dans notre projet, ce n'était pas la tâche.
Étape 10. Transfert de contenu statique vers wwwroot
Pour diffuser du
contenu statique, vous devez indiquer à l'hôte Web la racine du contenu du répertoire en cours. La valeur par défaut est wwwroot. Vous pouvez personnaliser votre répertoire pour stocker des fichiers statiques en configurant un middleware.
Étape 11. Portage d'EntityFramework vers EF Core
Si le projet utilise des
fonctionnalités spécifiques
d'Entity Framework 6 qui
ne sont
pas prises en
charge dans
EF Core
, il est logique d'exécuter l'application sur
NET Framework
. Dans ce cas, cependant, vous devez sacrifier la multiplateforme. L'application ne fonctionnera que sous Windows et sous IIS.
Jetons un coup d'œil aux principaux changements à prendre en compte:- Espace de noms System.Data.Entity remplacé par Microsoft.EntityFrameworkCore
- La signature du constructeur DbContext a été modifiée. Vous devez maintenant injecter DbContextOptions
- Méthode HasDatabaseGeneratedOption (DatabaseGeneratedOption.None) remplacée par ValueGeneratedNever ()
- WillCascadeOnDelete (false), méthode remplacée par OnDelete (DeleteBehavior.Restrict)
- Méthode OnModelCreating (DbModelBuilder modelBuilder) remplacée par la méthode OnModelCreating (ModelBuilder modelBuilder)
- La méthode HasOptional n'est plus disponible
- la configuration des objets est modifiée, vous devez maintenant utiliser OnModelCreating, car EntityTypeConfiguration n'est plus disponible
- L'attribut ComplexType n'est plus disponible
- Remplacement de l'interface IDbSet par DbSet
- ComplexType - la prise en charge des types complexes est apparue dans EF Core 2 avec le type Entité détenue ( https://docs.microsoft.com/en-us/ef/core/modeling/ownedentities ) et les tables sans clé primaire avec QueryType dans EF Core 2.1 ( https://docs.microsoft.com/en-us/ef/core/modeling/query-types )
- les clés étrangères dans EF Core génèrent des propriétés d'ombre en utilisant le modèle Id [Entity], contrairement à EF6, qui utilise le modèle _Id [Entity]. Par conséquent, ajoutez d'abord des clés étrangères en tant que propriété régulière à l'entité.
- Pour prendre en charge DI pour DbContext, configurez votre DbContex dans
ConfigureServices
Utilisez l'outil de
comparaison SQL pour vérifier que
EF Core
gĂ©nère un schĂ©ma de base de donnĂ©es similaire Ă
Entity Framework
lors de la
migration .
Étape 12. Suppression de toutes les références HttpContext, remplacement des classes obsolètes et modification de l'espace de noms
Lors de la migration de votre projet, vous constaterez qu'un nombre suffisamment important de classes ont été renommées ou déplacées, et vous devez maintenant tout mettre en conformité avec les nouvelles exigences. Voici une liste des principales transitions que vous pouvez rencontrer:
- HttpPostedFileBase -> IFormFile
- L'accès pour accéder à HttpContext se fait maintenant via IHttpContextAccessor
- HtmlHelper -> IHtmlHelper
- ActionResult -> IActionResult
- HttpUtility -> WebUtility
- Au lieu de HttpSessionStateBase - ISession, est accessible Ă partir de HttpContext.Session. de Microsoft.AspNetCore.Http
- Request.Cookies renvoie IRequestCookieCollection: IEnumerable <KeyValuePair <chaîne, chaîne >>, donc au lieu de HttpCookie, KeyValuePair <chaîne, chaîne> de Microsoft.AspNetCore.Http
Remplacement de l'espace de noms:
- SelectList -> Microsoft.AspNetCore.Mvc.Rendering
- UrlHelper -> WebUtitlity
- MimeMapping -> FileExtensionContentTypeProvider
- MvcHtmlString -> IHtmlString et HtmlString
- ModelState, ModelStateDictionary, ModelError -> Microsoft.AspNetCore.Mvc.ModelBinding
- FormCollection -> IFormCollection
- Request.Url.Scheme -> this.Url.ActionContext.HttpContext.Request.Scheme
Autre:
- MvcHtmlString.IsNullOrEmpty (IHtmlString) -> String.IsNullOrEmpty (variable.ToHtmlString ())
- [ValidateInput (false)] - ce n'est généralement plus et n'est pas nécessaire
- HttpUnauthorizedResult -> UnauthorizedResult
- [AllowHtml] - il n'y a plus de directive et ce n'est pas nécessaire
- La méthode TagBuilder.SetInnerText a été remplacée - c'est maintenant InnerHtml.AppendHtml
- JsonRequestBehavior.AllowGet lorsque le retour de Json n'est plus nécessaire
- HttpUtility.JavaScriptStringEncode. -> JavaScriptEncoder.Default.Encode
- Request.RawUrl. Il est nécessaire de connecter séparément Request.Path + Request.QueryString
- AllowHtmlAttribute - plus de classe
- XmlDownloadResult - maintenant vous pouvez utiliser simplement renvoyer File (Encoding.UTF8.GetBytes (xml), "application / xml", "filename.xml");
- [ValidateInput (false)] - il n'y a plus de directive et ce n'est pas nécessaire
Étape 13. Mise à jour de l'authentification et de l'autorisation
J'ai déjà écrit ci-dessus que dans notre projet, l'authentification n'est pas implémentée à l'aide du système d'identité intégré, mais est supprimée dans une couche distincte de middleware. Cependant, ASP.NET Core possède son propre mécanisme pour fournir des informations d'identification. Plus de détails peuvent être trouvés dans la documentation
ici .
Quant à la protection des données - nous n'utilisons plus
MachineKey . Au lieu de cela, nous utilisons la fonction de protection des données intégrée. Par défaut, les clés sont générées au démarrage de l'application. L'entrepôt de données peut être:
- Système de fichiers - fichier de clés basé sur le système de fichiers
- Azure Storage - Clés de protection des données dans Azure Blob Storage
- Redis - Clés de protection des données dans le cache Redis
- Registre - doit être utilisé si l'application n'a pas accès au système de fichiers
- EF Core - les clés sont stockées dans la base de données
Si les mécanismes intégrés ne conviennent pas, vous pouvez spécifier votre propre mécanisme de stockage de clés en fournissant un
IXmlRepository personnalisé.
Étape 14. Mise à jour de JS / CSS
La façon de travailler avec les ressources statiques a changé: maintenant, toutes doivent être stockées dans le dossier racine du projet
wwwroot , à moins, bien sûr, que d'autres paramètres ne soient spécifiés.
Lorsque vous utilisez des blocs en ligne javascript, il est recommandé de les déplacer à la fin de la page. Utilisez simplement l'attribut asp-location = "Footer" pour vos balises. La même règle s'applique aux fichiers js.
Utilisez l'extension
BundlerMinifier en remplacement de System.Web.Optimization - cela vous permettra de lier et de minimiser JavaScript et CSS lors de la construction du projet.
Lien vers la documentation.
Étape 15. Migration des vues
Les actions enfants ne sont plus utilisées. Au lieu de cela, ASP.NET Core propose un nouvel outil puissant -
ViewComponents , qui est appelé de manière asynchrone.
Comment obtenir une chaîne de ViewComponent:
Il n'est plus nécessaire d'utiliser HtmlHelper - ASP.NET Core dispose d'un grand nombre de fonctions de balise d'assistance (
Tag Helpers ) intégrées. Lorsque l'application est en cours d'exécution, elles sont traitées par le moteur Razor côté serveur et sont finalement converties en éléments html standard. Cela simplifie considérablement le développement d'applications. Et, bien sûr, vous pouvez implémenter vos propres balises.
Nous avons commencé à utiliser l'injection de dépendances dans les vues au lieu d'autoriser les paramètres et les services à l'aide de
EngineContext
.
Donc, les principaux points sur la migration des vues:
- Convertissez
Views/web.config Views/_ViewImports.cshtml
- utilisé pour importer des espaces de noms et Views/web.config Views/_ViewImports.cshtml
dépendances. Ce fichier ne prend pas en charge d'autres fonctions Razor
, telles que les définitions de fonctions et de sections. - Convertir
namespaces.add
en @using
- Transférez tous les paramètres dans la configuration principale de l'application
Scripts.Render
et Styles.Render
n'existe pas. Remplacez libman
ou BundlerMinifier
liens vers la sortie
En conclusion
Notre expérience nous a montré que le processus de migration d'une grande application Web est une tâche très chronophage qui peut difficilement être réalisée sans écueils. Nous avions prévu de passer à un nouveau framework dès la sortie de sa première version stable, mais nous n'avons pas pu le terminer tout de suite: il y avait certaines fonctions critiques qui n'avaient pas encore été transférées vers .NET Core, en particulier, liées à EntityFramework. Par conséquent, nous avons dû d'abord publier la prochaine version en utilisant une approche mixte - l'architecture .NET Core avec les dépendances .NET Framework.
Nous avons pu adapter complètement le projet après la sortie de .NET Core 2.1, ayant à l'époque une solution stable fonctionnant déjà sur la nouvelle architecture - il ne restait plus qu'à remplacer certains packages et réécrire le travail avec EF Core. Ainsi, la migration complète vers le nouveau framework a nécessité plusieurs mois de travail.
Vous pouvez en savoir plus sur notre projet dans notre
référentiel sur GitHub .