Aller au contenu principal

Migrer de 3.x vers 4.0

Cette page décrit, dans l'ordre, les étapes pour passer une application Lagoon 3.x en 4.0. La commande lgn update applique la plupart des changements. La page se concentre sur ce qu'il reste à faire à la main. Le détail de chaque changement se trouve dans le Changelog.

Applications en version bêta 4.0

Les règles de migration de la 4.0 ne s'appliquent pas aux applications déjà passées sur une version bêta 4.0.x : Faire uniquement lgn update.

1. Prérequis​

  • SDK .NET 10 installé : les applications passent de net8.0 à net10.0. .NET 6 n'est plus supporté.

  • Outil lgn 4.0.5 ou supérieur. Les fichiers de migration de la 4.0 l'exigent : avec un outil plus ancien, la mise à jour s'arrête en erreur.

    dotnet tool update LGN -g
  • Outil dotnet ef installé : la mise à jour génère une migration EF Core (voir étape 5).

  • Configuration du DbContext conforme au modèle Lagoon, sans quoi la migration LagoonMigrationV4 ne peut pas être générée correctement :

    • le projet Model contient le fichier Context/DesignTimeDbContextFactory.cs. dotnet ef l'utilise pour instancier le contexte, et la mise à jour le réécrit pour dériver de DesignTimeApplicationDbContextFactory<T> ;
    • Context/ApplicationDbContext.cs expose une méthode statique ApplicationDbContext.Configure(DbContextOptionsBuilder, IConfiguration, bool). Elle porte toute la configuration du contexte : provider, chaîne de connexion et appel UseOpenIddict(…) que la mise à jour convertit en UseOpenIddict<Guid>() ;
    • cette même méthode est appelée par Main.OnConfigureDbContext côté serveur et par la fabrique de design-time. Le modèle utilisé pour générer la migration est ainsi identique à celui de l'exécution.
    Server/Main.cs
    protected override void OnConfigureDbContext(DbContextOptionsBuilder builder)
    {
    ApplicationDbContext.Configure(builder, Configuration, ApplicationInformation.IsDevelopment);
    }
    Model/Context/DesignTimeDbContextFactory.cs (3.x)
    public ApplicationDbContext CreateDbContext(string[] args)
    {
    IConfiguration configuration = DesignTimeConfiguration.GetDevelopmentConfiguration();
    DbContextOptionsBuilder<ApplicationDbContext> builder = new();
    ApplicationDbContext.Configure(builder, configuration, true);
    return new ApplicationDbContext(builder.Options);
    }

    Si votre application configure le contexte autrement (options écrites directement dans OnConfigureDbContext, fabrique absente ou personnalisée), ramenez-la à ce modèle avant de lancer la mise à jour.

  • Une copie de travail propre (tout commité), pour relire le diff produit par la mise à jour.

  • Une sauvegarde de la base de données de chaque environnement avant d'appliquer la migration.

2. Lancer la mise à jour​

Depuis le dossier de la solution :

lgn update --allow-major --dotnet-fw
  • --allow-major autorise le passage à une nouvelle version majeure des packages Lagoon (3.x → 4.0). Sans cette option, lgn reste sur la dernière 3.x.
  • --dotnet-fw migre le framework .NET : cibles net10.0, packages Microsoft.* en 10.x, fichiers .vscode/ créés ou mis à jour.

Pour les grilles, la mise à jour fait d'abord une analyse sémantique du code : elle compile l'application pour déterminer le type réel des colonnes (TItem, TColumnValue).

3. Traiter les marqueurs laissés par la mise à jour​

Quand un changement ne peut pas être converti sans risque, lgn laisse un marqueur dans le code au lieu de deviner. Recherchez dans toute la solution :

  • LAGOON UNSUPPORTED : commentaire <-- LAGOON UNSUPPORTED: … --! qui explique l'action à faire ;
  • LAGOON_SET_TItem_Here : type volontairement invalide (erreur CS0246), à remplacer par le type de la ligne de la grille (TItem).
MarqueurCauseAction
LAGOON_SET_TItem_HereColonne ou gestionnaire OnCellClick dont le type de ligne n'a pas pu être déduit (plusieurs grilles de types différents dans le même fichier, ou analyse sémantique désactivée).Remplacer par le type des lignes de la grille, par exemple TItem="OrderRow" ou GridViewCellClickEventArgs<OrderRow>.
this template captures the Columns context variable …Un template CellContent / EditCellContent utilise la variable de <Columns Context="x">. En 4.0, cette variable désigne le modèle de ligne, pas la ligne affichée.Lire la ligne via le contexte de cellule : cell.Item.Xxx pour l'item, cell.Value pour la valeur liée. Voir Grille : rendu direct.
replace "InputMaskOptions" …Le paramètre de colonne InputMaskOptions est supprimé.Déclarer directement InputMask, InputMaskKind, InputMaskPlaceholder, AutoUnmask, ClearIncomplete ou Placeholder sur la colonne.
bare return in a now Task-returning handler …Un gestionnaire OnValueChange est devenu OnValueChangingAsync et renvoie désormais un Task.Remplacer return; par return Task.CompletedTask; (ou rendre la méthode async).
"MapStaticAssets()" now serves the static assets … / replace "UseStaticFiles()" …Main.cs du serveur appelle UseStaticFiles avec des options, ou n'a pas de bloc UseEndpoints.Voir étape 6.
review this generated migration …Migration LagoonMigrationV4 générée (clés OpenIddict).Voir étape 5.
Replace 'Roles' with 'Policy'[Authorize(Roles = …)] : l'autorisation par rôle n'est pas supportée (règle déjà présente en 3.x).Utiliser une policy.

4. Compiler et corriger les erreurs restantes​

Compilez la solution. Les erreurs attendues sont peu nombreuses et bien identifiées :

  • cannot convert 'X' to 'string' sur une ancienne LgGridColumn. La mise à jour l'a convertie en LgGridTextColumn, mais la valeur liée n'est pas une chaîne. Remplacez-la par la colonne spécialisée du type (LgGridNumericColumn, LgGridDateColumn, LgGridBooleanColumn, LgGridEnumColumn…) ou par LgGridCustomColumn. En général, l'analyse sémantique a déjà fait cette correction.
  • LgDynamicGridView introuvable : le composant est supprimé et aucune conversion automatique n'existe. Remplacez-le par une LgGridView<TItem> typée. Générez les colonnes variables avec une boucle @foreach dans <Columns>, liées par indexeur sur la ligne (@bind-Value="@item.Values[key]"), et fournissez une instance peuplée via ItemModel. Voir Colonnes dynamiques.
  • TItem demandé par le compilateur sur une colonne sans @bind-Value (une colonne définie par ValueSelector, par exemple) : ajoutez TItem="…" sur la colonne.
  • Aucun constructeur de LgEulaManager ne prend 3 arguments : le constructeur reçoit maintenant le service LgHtmlSanitizer. Ajoutez-le au code qui instancie ou dérive LgEulaManager. Voir CGU.

5. Base de données​

La mise à jour génère la migration EF Core LagoonMigrationV4 dans le projet Model. Relisez-la avant de l'appliquer (elle porte le marqueur review this generated migration), puis retirez le marqueur.

Ce que fait cette migration :

  • Clés OpenIddict en Guid : les entités OpenIddict utilisent désormais toujours une clé Guid (UseOpenIddict<Guid>(), réécrit automatiquement dans ApplicationDbContext) et passent au schéma d'OpenIddict 7. Si la clé était une string, les 4 tables OpenIddict sont vidées avant la conversion. Conséquence : tous les utilisateurs devront se reconnecter. Les applications et scopes OpenIddict sont réamorcés au démarrage.
  • Clés ASP.NET Identity : les colonnes de clé de AspNetUserTokens et AspNetUserLogins passent de 450 à 128 caractères. Vérifiez qu'aucune valeur existante ne dépasse cette longueur.
  • Nouvelle table EulaAcceptances : enregistrement de la date d'acceptation des conditions d'utilisation.
  • Sous SQL Server, MySQL/MariaDB et PostgreSQL, la migration libère puis recrée les clés et index portés par les colonnes converties, comme ces moteurs l'exigent.

Appliquez ensuite la migration comme d'habitude (voir Mise à jour de la base de données), d'abord sur un environnement de test, avec une sauvegarde.

Pour MySQL / MariaDB, le provider Pomelo est remplacé par son fork Microting.EntityFrameworkCore.MySql (automatique). Les migrations existantes restent valables.

Un code applicatif qui compare Database.ProviderName à une constante CollationProvider doit passer par DatabaseEngine.FromProviderName(…) : les constantes contiennent désormais un nom de moteur (SQLServer, MySQL…) et non plus un nom d'assembly.

6. Publication et fichiers statiques​

La publication ne produit plus que des variantes Brotli (.br) et supprime l'original de chaque fichier compressé. Seul MapStaticAssets sait servir ces fichiers.

  • Main.cs du serveur : la mise à jour remplace app.UseStaticFiles() par endpoints.MapStaticAssets() en tête du bloc UseEndpoints. Si un marqueur a été posé (appel avec options, ou pipeline sans UseEndpoints), faites la modification à la main :

    Main.cs (Server)
    app.UseBlazorFrameworkFiles();
    app.UseRouting();
    // ...
    app.UseEndpoints(endpoints =>
    {
    endpoints.MapStaticAssets();
    endpoints.MapRazorPages();
    endpoints.MapControllers();
    endpoints.MapLagoonFallback();
    });

    MapStaticAssets ne sert que les fichiers présents dans le manifeste de build : un fichier déposé dans wwwroot après la compilation n'est pas servi.

  • Application servie en HTTP (sans HTTPS) : Brotli n'est négocié que sur une origine sécurisée, le serveur répondrait 404 ou un corps vide. Posez LagoonDefaultCompression dans le Directory.Build.props de la solution :

    Directory.Build.props
    <Project>
    <PropertyGroup>
    <LagoonDefaultCompression>false</LagoonDefaultCompression>
    </PropertyGroup>
    </Project>
  • La propriété 3.x DisablePublishCleaning provoque désormais une erreur de compilation : retirez-la (et utilisez LagoonDefaultCompression si besoin).

Voir Compression des fichiers statiques.

7. Compilation et intégration continue​

L'outil lgn n'est plus nécessaire pour compiler une application Lagoon : les ressources (CSS, JS, icônes, dictionnaires, pages d'index) sont générées par les tâches MSBuild du package Lagoon.Build. Un poste de développement ou un agent de build a seulement besoin du SDK .NET. lgn reste utile pour ses autres commandes (new, update…).

8. Vérifier les changements de comportement silencieux​

Certains changements compilent sans erreur mais modifient le rendu. Vérifiez vos grilles :

Grille : rendu direct des cellules​

La grille exécute désormais le fragment <Columns> une seule fois, sur un modèle de ligne, puis rend chaque cellule avec des accesseurs compilés. Un paramètre de colonne qui lisait la variable de contexte (DisplayFormat="@(item.IsLate ? "N0" : "N2")", HideContent="@item.Masked"…) donne alors la même valeur sur toutes les lignes.

  • La mise à jour convertit les cas simples en sélecteurs par ligne (HideContentSelector, DisplayFormatSelector, PrefixSelector, CommandNameSelector…), mais uniquement quand le contexte s'appelle context et que la balise tient sur une ligne. Relisez les autres à la main : cherchez les paramètres de colonne qui utilisent la variable de <Columns Context="…">.
  • Un Value="@(context…)" sans @bind-Value devient un ValueSelector.
Revenir au rendu par ligne

Il n'y a pas d'option globale : la grille repasse automatiquement à l'ancien rendu, en exécutant <Columns> pour chaque ligne, dès qu'une colonne visible porte une valeur sans expression. C'est le cas quand la valeur est passée par Value="…" seul, sans @bind-Value ni ValueSelector. Ce mode reste disponible pour un template qui a vraiment besoin de capturer la ligne, mais il perd le gain de performance du rendu direct. Une telle colonne n'est ni triable ni filtrable, sauf si vous lui donnez un SortSelector / FilterSelector.

CGU au format .htm​

Le serveur assainit maintenant le contenu des CGU (mode dynamique et mode partagé). Dans un fichier .htm exporté d'un traitement de texte, les éléments style et l'attribut class sont conservés, mais meta, title, script et les formulaires sont supprimés. Vérifiez l'affichage de vos CGU. Voir Assainissement HTML côté serveur.

Paramètres de colonne retirés​

Les paramètres de filtre (AllowFilter, DefaultFilter, FilterExpression…), MaxLength et InputDisplayFormat ne sont plus déclarés sur les colonnes qui ne s'en servent pas. La mise à jour les retire. Attention : Razor ne signale pas un attribut inconnu. Vérifiez les attributs ajoutés à la main après la migration.

Ce que la mise à jour fait automatiquement​

Pour information, lgn update --allow-major --dotnet-fw applique notamment :

DomaineChangements
Projetsnet10.0, packages Microsoft.AspNetCore.* / *EntityFrameworkCore* en 10.x, Pomelo → Microting, versions EF Core épinglées dans Model et Services, SQLitePCLRaw.bundle_e_sqlite3 (CVE-2025-6965), AsyncFixer 2, suppression des packages obsolètes (Microsoft.AspNetCore.Http, Identity.UI, Mvc.NewtonsoftJson, System.Text.Json).
OutillageFichiers .vscode/ (launch.json, tasks.json, settings.json, extensions.json) créés et partagés via .gitignore ; bloc generated_code dans .editorconfig.
ServeurUseStaticFiles() → endpoints.MapStaticAssets().
Base de donnéesUseOpenIddict<Guid>(), migration LagoonMigrationV4 adaptée au moteur, DesignTimeDbContextFactory dérivée de DesignTimeApplicationDbContextFactory<T>.
ComposantsLgSearchNavigation → LgNavigationBox (sans Scrollable), LgInputRadioGroup / LgInputRadio → LgRadioGroup / LgRadio (Name → GroupName), OnChanging → OnChangingAsync (LgCheckBox, LgToggle), LgDateBox + DateBoxKind → LgWeekBox / LgMonthBox / LgYearBox, ChangingEventArgs → ChangingItemEventArgs, paramètre de type TValue sur LgOptionListItem, paramètres de masque retirés de LgSummaryBox.
GrilleLgGridColumn → LgGridTextColumn / LgGridCustomColumn, contexte de CellContent renommé cell, OnValueChange → OnValueChangingAsync (signature du gestionnaire comprise), OnCellClick typé GridViewCellClickEventArgs<TItem>, *Expression → *Selector, paramètres par ligne → *Selector, JsResizeColumnsAsync / RefreshCssVariablesAsync → AutoResizeColumnsAsync, Confirmation → ConfirmationMessage sur les colonnes, paramètres orphelins retirés.
StylesVariables SCSS $auth-app-loading-* → $app-loading-*.