Aller au contenu principal

Notes de version 4.0

Notes des versions 4.0.x du framework Lagoon, de la plus récente à la plus ancienne. Les versions marquées ⚠ contiennent des changements de rupture. Les autres versions sont listées sur la page Notes de version.

Version 4.0.1 ⚠​

Version corrective : durcissement de l'authentification SAML2 et support de plusieurs fournisseurs d'identité, éditeur HTML Jodit 4 et assainissement serveur des CGU, correctifs de sécurité des dépendances.

Authentification SAML2 ⚠​

La lecture des réponses SAML2 est durcie. Les contrôles suivants sont désormais toujours appliqués :

  • la signature doit porter sur le nœud qui la contient, avec un identifiant unique (protection contre le « signature wrapping ») ;
  • le statut de la réponse doit être un succès, et la réponse ne doit contenir qu'une seule assertion ;
  • l'assertion doit porter une confirmation bearer avec une date limite de livraison (NotOnOrAfter) et, sous RequireDestination, le Recipient de l'application ;
  • seules les valeurs signées satisfont les contrôles : la Destination et l'InResponseTo d'une enveloppe non signée ne suffisent plus ;
  • les signatures SHA-1 et les transformations non prévues par SAML sont refusées ;
  • les documents contenant une DTD sont refusés, et la taille décompressée (3 Mo) et la profondeur des documents sont bornées.

La requête d'authentification pose un cookie de corrélation : la réponse n'est acceptée que par le navigateur qui a envoyé la requête. En HTTP, l'application poste de nouveau la réponse vers elle-même, ce qui ajoute un aller-retour.

Les métadonnées du fournisseur d'identité doivent être téléchargées en HTTPS (RequireHttpsMetadata). Elles sont conservées 6 heures par emplacement. Si un nouveau téléchargement échoue, la version précédente reste utilisée pendant IdpMetadataFallbackDuration (1 jour par défaut), puis l'authentification échoue jusqu'au prochain téléchargement réussi.

Les valeurs d'attribut typées string, quel que soit le préfixe (xs:string, xsd:string de Shibboleth/OpenSAML…), et tous les AttributeStatement de l'assertion sont maintenant lus.

Le relay state est lié au nom du schéma d'authentification : les authentifications commencées avant la mise à jour doivent être relancées.

Plusieurs schémas SAML2 sont supportés, chacun avec son fournisseur d'identité :

  • la nouvelle option CallbackPath (/saml par défaut) donne à chaque schéma son chemin de retour et l'URL de ses métadonnées ; les chemins sont contrôlés au démarrage de l'application ;
  • la nouvelle option AllowedNameIdDomains limite les domaines des utilisateurs qu'un fournisseur d'identité peut connecter : les comptes sont retrouvés par e-mail, quel que soit le schéma. IsAllowedLogin applique la même règle dans une recherche de compte personnalisée ;
  • une assertion est identifiée par son émetteur et son identifiant dans le cache anti-rejeu ;
  • les métadonnées partagées par plusieurs schémas respectent la durée de repli de chacun.

Action :

  • Un fournisseur d'identité qui signe en SHA-1 est refusé : passez-le en SHA-256, ou posez options.Validation.AllowSha1 = true.
  • Des métadonnées téléchargées par une URL http:// sont refusées : passez à https:// ou à un fichier local, ou posez options.RequireHttpsMetadata = false pour un fournisseur d'identité de développement.
  • Un code qui lit lui-même une réponse avec SamlResponse (ou SamlResponse.FromHttpRequest) sans ForRequest(…) échoue désormais : fournissez l'identifiant de la requête et l'URL de retour avec Validation.ForRequest(requestId, acsUrl), ou désactivez explicitement RequireDestination / RequireInResponseTo.
  • ⚠ Une application qui déclare déjà plusieurs schémas SAML2 (les suivants étaient inopérants) ne démarre plus : donnez à chacun, sauf un, son CallbackPath (par exemple /saml-partner), et enregistrez la nouvelle URL auprès de son fournisseur d'identité. Une application à un seul schéma n'a rien à changer.
  • Avec plusieurs fournisseurs d'identité, renseignez AllowedNameIdDomains sur ceux qui ne gèrent pas tous les utilisateurs de l'application.

Voir Authentification SAML2 pour le détail des contrôles.

Éditeur HTML et CGU : correctifs de sécurité ⚠​

LgHtmlEditor passe de Jodit 3.24.5 à Jodit 4.15.14, qui corrige plusieurs failles XSS de l'éditeur (collage de HTML, mode source, liens javascript:). L'assistant IA de Jodit 4 (boutons « AI Commands » et « AI Assistant ») est désactivé. Les composants de date (LgDateBox, LgMonthBox, LgWeekBox, LgYearBox) ne chargent plus le script de Jodit, qu'ils n'utilisaient pas.

Le nouveau service serveur LgHtmlSanitizer (Lagoon.Server) assainit un contenu HTML : il retire les scripts, les gestionnaires d'événements, les URL dangereuses, les formulaires et les cadres. Lagoon l'applique aux CGU, à l'écriture (SetEula) et à la lecture (GetAllEula, fichiers du mode partagé compris), sans modifier la base de données. Les clients téléchargent une fois de nouveau les CGU qu'ils avaient en cache, sans revalidation.

Action :

  • Le constructeur de LgEulaManager reçoit un paramètre LgHtmlSanitizer : adaptez le code qui l'instancie directement ou qui en dérive. Une implémentation personnalisée de ILgEulaManager doit assainir elle-même le contenu.
  • Vérifiez le rendu de vos CGU au format .htm : les éléments style et l'attribut class sont conservés, mais meta, title, script et les formulaires sont supprimés.
  • Assainissez côté serveur les autres contenus saisis avec LgHtmlEditor avant de les afficher à d'autres utilisateurs.

Voir Assainissement HTML côté serveur et CGU.

Corrections​

  • Un Prefix / Suffix sans PrefixType / SuffixType explicite est de nouveau affiché comme du texte : depuis la 4.0.0, il était interprété comme un nom d'icône (Suffix="kg" n'affichait rien). Concerne les champs de saisie et les colonnes de grille qui exposent ces paramètres.

Sécurité​

Cette version met à jour les dépendances suivantes :

  • PuppeteerSharp 25.12.0 (Chrome 154, plus de 100 correctifs de sécurité du navigateur) : les applications utilisant Lagoon.PDF avec téléchargement automatique du navigateur récupèrent la version corrigée ; celles qui fournissent leur propre Chrome/Chromium (Lagoon:Puppeteer:ExecutablePath, image Docker) doivent le mettre à jour.
  • MessagePack 3.1.10 : corrige GHSA-qhrr-8q5h-9q3h (CVE-2026-92707, amplification mémoire lors de la désérialisation en object) et GHSA-fx6j-x9m5-vmrf (ReDoS avec AllowAssemblyVersionMismatch). Lagoon n'est pas directement exposé, mais les hubs SignalR applicatifs acceptant des paramètres object l'étaient.
  • Éditeur HTML (LgHtmlEditor) : Jodit 4.15.14 : corrige les failles XSS et de pollution de prototype publiées en 2026 (dont GHSA-rxcw-mc6f-6hr3 et GHSA-w3xv-x3fm-59ph).
  • CGU assainies côté serveur : voir la section Éditeur HTML et CGU ci-dessus. LgHtmlSanitizer s'appuie sur HtmlSanitizer 9.2.1039 et AngleSharp 1.8.2, qui corrige GHSA-cgp3-27rh-pcp2 (mXSS à la sérialisation, non exploitable avec la configuration par défaut de LgHtmlSanitizer).
  • .NET 10.0.12 : alignement sur le Patch Tuesday de septembre 2026. Les correctifs sont dans le runtime et le SDK, pas dans les paquets : installez l'ASP.NET Core Runtime / Hosting Bundle 10.0.12 sur les serveurs (en particulier sous IIS, CVE-2026-69304) et le SDK 10.0.112 / 10.0.401 ainsi que Visual Studio à jour sur les postes et la CI (CVE-2026-58649, CVE-2026-69806, CVE-2026-69439, CVE-2026-71328, CVE-2026-69522). Les applications utilisant l'authentification SAML de Lagoon.Server doivent en outre s'assurer que leurs serveurs n'exécutent pas un runtime antérieur à 10.0.10 (correctifs System.Security.Cryptography.Xml de juillet 2026).

Dépendances​

  • Paquets ASP.NET Core, EF Core et Microsoft.Extensions.* en 10.0.12 ; OpenIddict 7.7.1 ; Microsoft.IdentityModel.* 8.23.0 ; MailKit 4.18.1 ; AWSSDK.S3 4.0.103.4.
  • La copie Lagoon du script d'authentification Blazor (AuthenticationService.js) est alignée sur la version 10.0.12 du package Microsoft.AspNetCore.Components.WebAssembly.Authentication, y compris pour les applications net8.0.

Version 4.0.0 ⚠​

Version majeure : passage à .NET 10, refonte du rendu de la grille, OpenIddict 7 et nouvelle chaîne de build sans outil externe.

Mettre à niveau une application 3.x

Suivez la page Migrer de 3.x vers 4.0 : prérequis, commande lgn update --allow-major --dotnet-fw, liste des actions manuelles, migration de la base de données et publication. Les sections ci-dessous décrivent chaque changement. Les mentions Action signalent ce que la mise à jour automatique ne fait pas.

Plateforme et dépendances​

  • Les applications ciblent .NET 10 (net10.0, LangVersion 12). Les modules sont publiés pour net8.0 et net10.0. .NET 6 n'est plus supporté.
  • Dépendances majeures : EF Core 10, OpenIddict 7 (depuis 4.10), MessagePack 3 (depuis 2.5).
  • La montée de version nécessite lgn 4.0.5 ou supérieur et l'option --allow-major : lgn update --allow-major --dotnet-fw.
  • appsettings.json est désormais facultatif au design-time (commandes dotnet ef).

Build : lgn n'est plus nécessaire pour compiler​

Les ressources d'une application (CSS compilé depuis le SCSS, bundle JavaScript, icônes, dictionnaires fusionnés, pages d'index) sont générées par les tâches MSBuild du package Lagoon.Build. Elles remplacent l'appel à lgn build et le générateur de sources. Un poste de développement ou un agent de build n'a donc plus besoin de l'outil lgn : seul le SDK .NET est requis. lgn reste utile pour ses autres commandes (new, update…).

La compilation Release minifie le JavaScript avec Terser : elle nécessite node.js (Terser est installé automatiquement au premier usage). Le CSS est minifié en Release et reste lisible en Debug (propriété LagoonMinifyCss pour forcer l'un ou l'autre).

Grille​

Rendu direct des cellules ⚠​

La grille exécute désormais le fragment <Columns> une seule fois, sur un modèle de ligne, avant le premier rendu. Elle rend ensuite chaque cellule directement depuis l'état des colonnes, avec des accesseurs compilés. Le gain de performance est important, surtout sur les grandes pages.

Conséquence : la variable de contexte de <Columns Context="item"> désigne le modèle, pas la ligne affichée. Un paramètre de colonne qui la lisait (DisplayFormat="@(item.IsLate ? "N0" : "N2")", HideContent="@item.Masked"…) n'est plus réévalué par ligne. La casse est silencieuse : le code compile, mais toutes les lignes affichent la valeur du modèle.

  • Dans les templates CellContent / EditCellContent, lisez la ligne via le contexte de cellule, renommé cell : cell.Item pour l'item de la ligne, cell.Value pour la valeur liée. Les colonnes sans valeur (sélection, commande, édition, séparateur) exposent aussi @context.Item.
  • Pour un paramètre qui varie selon la ligne, utilisez sa variante <Param>Selector (voir ci-dessous).
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 d'une valeur passée par Value="…" seul, sans @bind-Value ni ValueSelector. Les captures du contexte fonctionnent alors comme en 3.x, mais sans le gain de performance du rendu direct. Une telle colonne n'est ni triable ni filtrable, sauf avec un SortSelector / FilterSelector.

Action : la mise à jour convertit les cas simples en sélecteurs. Elle marque d'un LAGOON UNSUPPORTED les templates qui capturent la variable de <Columns>. Relisez les paramètres de colonne qui l'utilisent encore.

Sélecteurs par ligne​

Chaque paramètre de colonne qui peut varier selon la ligne dispose d'une variante <Param>Selector, un délégué Func<TItem,…> évalué pour chaque ligne :

ParamètreSélecteurColonne
HideContentHideContentSelectortoutes
DisplayFormatDisplayFormatSelector (affichage seul)toutes
CommandNameCommandNameSelectorLgGridCommandColumn
CommandTooltipCommandTooltipSelectorLgGridCommandColumn
IconNameIconNameSelectorLgGridCommandColumn
PrefixPrefixSelectorLgGridNumericColumn
SuffixSuffixSelectorLgGridNumericColumn
MinMinSelector (édition seule)LgGridDateColumn
MaxMaxSelector (édition seule)LgGridDateColumn
<LgGridNumericColumn @bind-Value="item.Amount"
DisplayFormatSelector="i => i.IsLate ? "N0" : "N2""
PrefixSelector="i => i.Currency" />
<LgGridCommandColumn CommandNameSelector="i => i.CanValidate ? "Validate" : """ />

Règle : le sélecteur gagne. S'il est fourni, son résultat par ligne est utilisé ; le paramètre statique ne sert que de valeur par défaut quand aucun sélecteur n'est posé. CssClassSelector (additif) et CanEditSelector (combiné en ET) gardent leur sémantique propre. Au niveau de la grille, RowCanEditSelector (Func<TItem, bool>) autorise ou interdit l'édition d'une ligne entière. Sur une grille avec ligne de calcul, les sélecteurs de lecture sont appelés avec un item null : un sélecteur qui lit l'item doit gérer ce cas.

La mise à jour réécrit Xxx="@(context…)" en XxxSelector="@(context => …)", mais seulement quand le contexte s'appelle context et que la balise tient sur une ligne. Les autres cas sont à reprendre à la main.

Sélecteurs de valeur et d'expression​

Ces sélecteurs sont des expressions (Expression<Func<TItem,…>>), pas des délégués : ils alimentent la valeur, le tri, le filtre, le groupement et l'export. Ils doivent rester traduisibles côté serveur sur une grille distante ([Queryable]). Ils sont figés une seule fois, au chargement de la colonne.

  • ValueSelector (Expression<Func<TItem,TColumnValue>>) : alternative à @bind-Value, sans capture de la variable de contexte. Il fournit une valeur complète (lecture, tri, filtre, groupe, export). Une colonne déclare soit @bind-Value soit ValueSelector. Un chemin de membre simple (i => i.Amount) reste éditable et traduisible côté serveur. Une expression calculée (i => i.Qty * i.Price) rend la colonne lecture seule et limitée au tri/filtre local.
  • SortSelector / GroupSelector / LambdaSelector remplacent SortExpression / GroupExpression / LambdaExpression, désormais obsolètes. Poser un SortSelector (ou un LambdaSelector) active le tri, et LambdaSelector sert aussi de clé de filtre par défaut.
  • FilterSelector (colonnes filtrables) remplace FilterExpression. Le poser active le filtre et fournit sa clé.
  • WeightSelector remplace WeightExpression : poids de la moyenne pondérée (CalculationType="Average").

Une clé de type valeur est automatiquement « déboxée » pour rester traduisible côté serveur.

<LgGridNumericColumn TItem="Order" TColumnValue="decimal"
ValueSelector="o => o.Quantity * o.UnitPrice" Title="Total" />
<LgGridTextColumn @bind-Value="o.Reference"
SortSelector="o => o.CreatedOn" GroupSelector="o => o.Customer" />

La mise à jour transforme un Value="@(context…)" sans @bind-Value en ValueSelector et renomme les *Expression en *Selector. Une lambda écrite en clair est conservée, et le wrapper GetLambdaExpression(…) est remplacé par la lambda inline. Une forme @(GetLambdaExpression(…)), ou une expression lue dans une variable, reste à reprendre à la main. Sans @bind-Value, ajoutez TItem="…" si le compilateur le demande.

LgGridColumn scindée en LgGridTextColumn et LgGridCustomColumn ⚠​

LgGridColumn est supprimée et remplacée par deux composants :

AvantAprèsQuand
<LgGridColumn @bind-Value="item.Name" /><LgGridTextColumn @bind-Value="item.Name" />La valeur liée est une string.
<LgGridColumn @bind-Value="item.Reference" /><LgGridCustomColumn @bind-Value="item.Reference" />La valeur liée est d'un autre type (type métier, Guid…).

LgGridTextColumn n'a plus qu'un paramètre de type (TItem, fourni par la grille) ; LgGridCustomColumn<TItem, TColumnValue> garde les deux. LgGridCustomColumn n'a pas de filtre : l'ancienne LgGridColumn imposait un filtre texte à toutes les valeurs. La cellule de filtre reste vide et un DefaultFilter éventuel est ignoré. Utilisez la colonne spécialisée du type, ou le template FilterCellContent. En contrepartie, LgGridCustomColumn applique DisplayFormat aux valeurs IFormattable.

La mise à jour convertit les colonnes. Elle s'appuie sur une analyse sémantique pour retrouver le type réel des valeurs liées. Une colonne non textuelle mal convertie provoque une erreur cannot convert 'X' to 'string'. Remplacez-la alors par la colonne spécialisée (LgGridNumericColumn, LgGridDateColumn, LgGridBooleanColumn, LgGridEnumColumn…) ou par LgGridCustomColumn. Un type de ligne non déduit laisse le marqueur LAGOON_SET_TItem_Here (erreur CS0246).

LgDynamicGridView supprimée ⚠​

LgDynamicGridView et ses colonnes LgDynamicGrid…Column sont supprimées. Une grille dont le nombre de colonnes varie devient une LgGridView<TItem> typée. Les colonnes sont générées par une boucle @foreach / @for dans <Columns> et liées par indexeur sur la ligne (@bind-Value="@item.Absences[day]"). Une instance peuplée est passée par ItemModel pour fixer le nombre de colonnes. Voir Colonnes dynamiques.

Action : aucune conversion automatique. Remplacez chaque LgDynamicGridView à la main.

Nettoyage des paramètres de colonne​

Les paramètres de filtre ne sont plus déclarés sur toutes les colonnes. Ils sont déplacés sur la base LgGridBaseFilterableColumn, dont héritent uniquement les colonnes qui exposent un filtre.

ParamètreDéclaré à partir de la 4.0 sur…N'existe plus sur…
AllowFilter, DefaultFilter, FilterExpression, FilterShowAllItems, FilterDisableSelection, FilterCellContent, MinSearchFilterCharacterles colonnes filtrables (texte, numérique, date, heure, booléenne, énum, select, tagbox, summary…)LgGridCustomColumn, LgGridColorColumn, LgGridImageColumn, LgGridCommandColumn, LgGridEditColumn, LgGridSelectionColumn, LgGridSeparatorColumn
MaxLengthles colonnes texte et summarytoutes les autres colonnes
InputDisplayFormatLgGridNumericColumn (seule cellule à le lire)toutes les autres colonnes, où il était sans effet

Razor ne signale pas un attribut qui ne correspond à aucun paramètre du composant. Un MinSearchFilterCharacter ou un MaxLength restés sur une colonne qui ne les déclare pas lèvent une InvalidOperationException au rendu de la grille. La mise à jour les retire des colonnes concernées.

Masque de saisie et placeholder par colonne​

La classe InputMaskOptions et le paramètre de colonne du même nom sont supprimés ([Obsolete(…, true)]). Les options du masque se déclarent directement sur la colonne, et chaque colonne n'expose que ce que son éditeur sait faire :

ColonneParamètres
Texte (LgGridTextColumn)Placeholder, InputMask, InputMaskKind, InputMaskPlaceholder, AutoUnmask, ClearIncomplete (+ MaxLength)
SummaryPlaceholder (+ MaxLength)
TagBoxles mêmes que Texte, plus ses propres paramètres d'étiquettes
Select, SelectMultiple, Enum, EnumListPlaceholder, InputMask, InputMaskKind, InputMaskPlaceholder
Numeric, Date, Month, Week, Year, TimePlaceholder
Custom, Image, Color, Boolean, Command, Edit, Selection, Separatoraucun

InputMaskPlaceholder reste distinct de Placeholder. Il fixe le caractère qui remplit les positions vides du masque : avec # et le masque 99-99-99-99-99, le champ affiche ##-##-##-##-##. Placeholder reste le texte indicatif du champ vide. Il est disponible sur toutes les colonnes à masque, ainsi que sur LgTextBox, LgSelect, LgSelectMultiple, LgSuggestBox et LgTagBox.

Le masque ne s'applique qu'au mode texte simple de LgTextBox. LgSummaryBox et LgGridSummaryColumn perdent donc leurs paramètres de masque : leur éditeur est une zone multiligne, où le masque n'a jamais eu d'effet. LgTextFilterBox suit la même logique : sa propriété InputMaskOptions est remplacée par les paramètres séparés.

Le template FilterCellContent est réparé : il faisait planter le rendu de toute grille qui l'utilisait. Son contexte est FilterContext, dont la propriété Filter est lisible et modifiable.

Action : la mise à jour marque chaque InputMaskOptions="…" d'un LAGOON UNSUPPORTED. La valeur peut être construite n'importe où, elle n'est donc pas convertible automatiquement : déclarez les paramètres séparés sur la colonne.

Événements et méthodes​

  • OnValueChange devient OnValueChangingAsync (GridViewValueChangingEventArgs) : le gestionnaire renvoie un Task. La mise à jour adapte la signature. Action : un return; nu est marqué et doit renvoyer Task.CompletedTask.
  • OnCellClick reçoit un GridViewCellClickEventArgs<TItem> typé.
  • JsResizeColumnsAsync et RefreshCssVariablesAsync sont fusionnées dans AutoResizeColumnsAsync.

Nouveautés​

  • Virtualisation des lignes (Virtualize, VirtualizeItemSize) : seules les lignes visibles sont rendues, ce qui est utile avec de grandes pages (PageSize = 0). Elle est désactivée par défaut. On l'active par grille (Virtualize="true") ou pour toute l'application :

    Main.cs (Client)
    app.GridView(options => options.Virtualize = true);

    Elle ne s'applique pas à une grille groupée. Voir Virtualisation des lignes.

  • Cache des données locales filtrées et triées entre deux chargements de page.

  • Jeux de colonnes réutilisables (LgGridColumnSet) : un composant peut regrouper des colonnes et être placé dans <Columns>. Les colonnes peuvent aussi être placées dans un LgGroup ou un LgAuthorizeView.

Composants de saisie​

Renommages (appliqués par la mise à jour) :

AvantAprès
LgSearchNavigationLgNavigationBox (le paramètre Scrollable disparaît)
LgInputRadioGroup / LgInputRadioLgRadioGroup / LgRadio (Name → GroupName)
OnChanging (LgCheckBox, LgToggle)OnChangingAsync
ChangingEventArgsChangingItemEventArgs
<LgDateBox DateBoxKind="Week/Month/Year">LgWeekBox / LgMonthBox / LgYearBox
LgOptionListItem TItemValue="…"LgOptionListItem TValue="…"

Nouveautés :

  • LgPasswordBox : champ de saisie de mot de passe.
  • LgWeekBox et LgMonthBox : saisie d'une semaine ou d'un mois, sur le modèle de LgYearBox (remplacent DateBoxKind).
  • CascadingTypeParameter sur LgSelect et LgSelectMultiple : le type de valeur est transmis aux options enfants, qui n'ont plus besoin de le déclarer.
  • Refonte interne de LgSelect, des listes déroulantes, du sélecteur de couleur et des composants de saisie (sans changement d'API), avec de nombreuses corrections (ouverture/fermeture des listes déroulantes, position des libellés, restauration de la valeur après annulation d'une confirmation).

Application​

  • Écran de chargement unique : il reste affiché de la première image jusqu'à ce que l'application soit prête, au lieu d'être suivi d'un second écran « Authorizing… ». Son texte et le titre de la page reprennent le nom de l'application (réglage App:Name). La mise à jour adapte index.html et renomme les variables SCSS $auth-app-loading-* en $app-loading-*.
  • Spinner personnalisable : 17 animations de chargement au choix, pour l'écran de démarrage et LgLoaderView. Le spinner et ses couleurs se choisissent dans le main.scss du client ($spinner, $sp-color, $sp-color-alt, $sp-bg-color, $sp-size). La page Spinner de chargement présente la galerie des spinners et génère le bloc SCSS.
  • EULA : case à cocher d'acceptation sur la page des conditions d'utilisation, et enregistrement de la date d'acceptation (nouvelle table EulaAcceptances).
  • OnConfigureHttpClient(IHttpClientBuilder, string) : nouvelle méthode de la classe Main du client pour configurer les HttpClient de l'application.
  • Nouvelle échelle de z-index (surcharges Bootstrap) pour l'empilement des listes déroulantes et des fenêtres modales.
  • Police Poppins livrée en WOFF2.

Serveur et base de données​

OpenIddict 7 et clés Guid ⚠​

Les entités OpenIddict utilisent désormais toujours une clé Guid, quel que soit le type de clé de l'utilisateur : builder.UseOpenIddict<Guid>(). Un UseOpenIddict() sans argument empêche l'application de démarrer. La mise à jour réécrit l'appel et génère la migration LagoonMigrationV4, adaptée à chaque moteur (SQL Server, MySQL/MariaDB, PostgreSQL, SQLite). Cette migration couvre aussi le schéma d'OpenIddict 7, les clés ASP.NET Identity ramenées à 128 caractères et la table EulaAcceptances.

Action : relisez la migration avant de l'appliquer (elle porte un marqueur LAGOON UNSUPPORTED) et sauvegardez la base. Quand la clé OpenIddict était une string, les tables OpenIddict sont vidées : tous les utilisateurs devront se reconnecter. Voir Migrer de 3.x vers 4.0.

Fabrique de contexte au design-time​

La fabrique DesignTimeDbContextFactory de l'application dérive désormais de DesignTimeApplicationDbContextFactory<T>. La classe de base charge la configuration appsettings*.json et construit le DbContextOptionsBuilder, avec les mêmes options Identity qu'à l'exécution. Cela supprime l'avertissement PendingModelChangesWarning au démarrage. DesignTimeConfiguration.GetDevelopmentConfiguration() est obsolète (erreur de compilation). La mise à jour réécrit la fabrique.

MySQL / MariaDB : Pomelo remplacé par Microting​

Pomelo ne publie rien pour .NET 10 : le paquet Pomelo.EntityFrameworkCore.MySql est remplacé par son fork Microting.EntityFrameworkCore.MySql. Le fork ne renomme que l'assembly et les espaces de noms. Le SQL généré, les annotations de modèle (MySql:CharSet, MySql:ValueGenerationStrategy…) et les migrations existantes sont inchangés. La mise à jour remplace le PackageReference et les using.

Les constantes CollationProvider nomment le moteur​

Un moteur peut être servi par plusieurs providers : les constantes de CollationProvider contiennent désormais un nom de moteur stable, et non plus le nom de l'assembly du provider.

ConstanteValeur 3.xValeur 4.0
CollationProvider.SQLServerMicrosoft.EntityFrameworkCore.SqlServerSQLServer
CollationProvider.OracleOracle.EntityFrameworkCoreOracle
CollationProvider.MySQLPomelo.EntityFrameworkCore.MySqlMySQL
CollationProvider.PostgreSQLNpgsql.EntityFrameworkCore.PostgreSQLPostgreSQL
CollationProvider.SQLiteMicrosoft.EntityFrameworkCore.SqliteSQLite
CollationProvider.MariaDBMariaDBMariaDB (inchangé)

DbContextOptionsBuilder.GetProviderName() retourne ces mêmes noms. La nouvelle méthode DatabaseEngine.FromProviderName(providerName) convertit un nom de provider EF Core (DbContext.Database.ProviderName) en nom de moteur.

Action : un code qui comparait une de ces constantes à Database.ProviderName doit passer par DatabaseEngine.FromProviderName(…). Une comparaison du type GetProviderName() == CollationProvider.MySQL reste correcte.

Publication : compression et fichiers statiques ⚠​

Le toolkit remplace les formats de compression par défaut du SDK : aucune compression à la compilation, et Brotli seul à la publication. Le SDK produisait .gz à la compilation et .gz + .br à la publication. À la publication, le fichier original de chaque élément qui a une variante .br est supprimé. Sur l'application de référence, le wwwroot publié passe de 23,8 Mo à 10,4 Mo. Ne sont jamais supprimés : les fichiers sans variante compressée (images, polices) et ceux que le runtime Lagoon lit par leur nom non compressé.

Le serveur sert donc les fichiers statiques avec endpoints.MapStaticAssets() au lieu de app.UseStaticFiles(). MapStaticAssets négocie la variante compressée et gère les ETag et les en-têtes de cache. Il ne sert que les fichiers présents dans le manifeste de build. La mise à jour fait le remplacement dans Main.cs. Un UseStaticFiles avec options, ou un pipeline sans bloc UseEndpoints, est marqué et doit être adapté à la main.

Action :

  • Une application servie en HTTP ne reçoit plus aucun fichier statique, car Brotli n'est négocié que sur une origine sécurisée. Posez LagoonDefaultCompression à false dans le Directory.Build.props de la solution : les variantes gzip sont rétablies et les fichiers non compressés conservés.
  • La propriété 3.x DisablePublishCleaning provoque une erreur de compilation : retirez-la.
Directory.Build.props
<Project>
<PropertyGroup>
<LagoonDefaultCompression>false</LagoonDefaultCompression>
</PropertyGroup>
</Project>

Voir Compression des fichiers statiques pour le détail des formats, la liste des exclusions et la procédure de vérification.