AnthoDingo.Update 1.0.0

dotnet add package AnthoDingo.Update --version 1.0.0
                    
NuGet\Install-Package AnthoDingo.Update -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AnthoDingo.Update" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AnthoDingo.Update" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="AnthoDingo.Update" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AnthoDingo.Update --version 1.0.0
                    
#r "nuget: AnthoDingo.Update, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AnthoDingo.Update@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AnthoDingo.Update&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=AnthoDingo.Update&version=1.0.0
                    
Install as a Cake Tool

AnthoDingo.Update

Français · English

Garde de mise à niveau de base de données pour ASP.NET Core + Blazor.

Tant que des migrations EF Core sont en attente, toute requête est redirigée vers une page /update fournie par la bibliothèque, qui liste les migrations restantes et permet à un administrateur de confirmer leur application. Le schéma n'est donc jamais migré silencieusement au démarrage, et aucune page ne s'exécute sur un schéma obsolète.

  • Page Blazor intégrée : composant routable /update, mise en page vide, styles et icône portés par le composant — ni Bootstrap ni feuille de style de l'hôte requis.
  • Agnostique du DbContext : AddDatabaseUpdate<TDbContext>(), ou votre propre IMigrationManager si vos migrations ne viennent pas d'EF Core.
  • Pas de round-trip inutile : une fois la base à jour, la garde se désactive pour la durée de vie du processus.
  • Tolère une installation différée : si le DbContext n'est pas encore enregistré (assistant de premier démarrage type AnthoDingo.Setup), la garde laisse passer les requêtes.

Cible : net8.0 et net10.0. Dépendance : Microsoft.EntityFrameworkCore.Relational (le provider reste le choix de l'application).

Une application de démonstration complète — Blazor Web App sur SQLite, livrée avec deux migrations en attente — se trouve dans src/AnthoDingo.Update.Sample :

dotnet run --project src/AnthoDingo.Update.Sample

Utilisation

1. Enregistrer et brancher

using AnthoDingo.Update;

builder.Services.AddDbContext<AppDbContext>(o => o.UseSqlServer(connectionString));

builder.Services.AddDatabaseUpdate<AppDbContext>(options =>
{
    options.ProductName = "Mon Application";

    // Chemins nécessaires pour atteindre /update, que la garde ne doit pas intercepter.
    options.ExemptPathPrefixes.Add("/Account");

    // Fortement recommandé : réserver la confirmation à un utilisateur authentifié,
    // et éventuellement à une politique d'autorisation.
    options.RequireAuthentication = true;
    options.SignInPath = "/Account/Login";
    // options.AuthorizationPolicy = "AdminOnly";
});

var app = builder.Build();

app.UseMigrationsGate();   // avant UseRouting

app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

2. Rendre la page routable

Le composant vit dans l'assembly de la bibliothèque : il faut le déclarer au routeur.

Blazor Web App (.NET 8+) — au point de montage des composants :

app.MapRazorComponents<App>()
   .AddInteractiveServerRenderMode()
   .AddAdditionalAssemblies(ServiceCollectionExtensions.UpdateAssembly);

Blazor Server classique (App.razor portant le Router) :

@using AnthoDingo.Update

<Router AppAssembly="@typeof(App).Assembly"
        AdditionalAssemblies="@(new[] { ServiceCollectionExtensions.UpdateAssembly })">
    ...
</Router>

3. Mode de rendu

⚠️ Le bouton Confirmer la mise à niveau exige un mode de rendu interactif côté serveur. Dans une Blazor Web App, le rendu statique (SSR) est le défaut : la page s'affiche et liste les migrations, mais le bouton ne déclenche rien.

Deux façons de le garantir :

// a) interactivité globale, déclarée sur App.razor
<Routes @rendermode="InteractiveServer" />

// b) ou, si l'interactivité est décidée page par page, activez-la au moins pour /update

Le mode WebAssembly n'est pas supporté : la page applique les migrations en appelant IMigrationManager, qui n'existe que côté serveur.

Chemins exemptés

La garde laisse toujours passer /update, /_blazor, /_framework, /_content, le options.SignInPath s'il est renseigné, et les requêtes portant une extension de fichier statique connue (.css, .js, .png, .woff2…, voir UpdateOptions.DefaultExemptFileExtensions, complétable via options.ExemptFileExtensions).

La liste d'extensions est volontairement fermée : une route applicative peut contenir un point (/utilisateurs/jean.dupont, /api/v1.0/articles) sans être un fichier statique, et doit rester derrière la garde.

Ajoutez-y les chemins d'authentification de votre application. La garde s'exécute avant UseAuthentication et ne peut pas distinguer une requête d'authentification d'une requête applicative : sans exemption, l'accès à /update boucle quand la page est protégée par [Authorize] — la redirection vers la page de connexion est elle-même renvoyée vers /update. Pour un fournisseur externe (OIDC), pensez aussi aux chemins de callback :

options.ExemptPathPrefixes.Add("/Account");
options.ExemptPathPrefixes.Add("/authorization-code/callback");
options.ExemptPathPrefixes.Add("/signout-callback-oidc");

Comportement de la redirection

Requête Réponse de la garde
GET / HEAD 302 vers {PathBase}/update?returnUrl=... — l'utilisateur est ramené à l'URL demandée une fois la mise à niveau appliquée
POST, PUT, DELETE… 503 avec un en-tête Location — une redirection transformerait la requête en GET et perdrait son corps
Base injoignable 302 vers /update, qui affiche l'erreur ; le détail part dans les journaux

Le PathBase est préservé : l'application peut être hébergée dans un répertoire virtuel.

Qui peut appliquer les migrations

Deux réglages, cumulables, décident si le bouton Confirmer la mise à niveau est proposé :

options.RequireAuthentication = true;         // il faut être connecté
options.SignInPath = "/Account/Login";        // lien proposé pour se connecter
options.AuthorizationPolicy = "AdminOnly";    // et satisfaire cette politique
RequireAuthentication AuthorizationPolicy Visiteur anonyme Connecté, hors politique Connecté, dans la politique
false null (défaut) ✅ peut appliquer ✅ ✅
true null 🔑 invité à se connecter ✅ ✅
false "AdminOnly" 🔑 invité à se connecter ⛔ refus ✅
true "AdminOnly" 🔑 invité à se connecter ⛔ refus ✅

Dans tous les cas la liste des migrations en attente reste visible : seul le bouton de confirmation disparaît. L'authentification est vérifiée avant la politique, et un visiteur anonyme qui échoue la politique est invité à se connecter plutôt qu'éconduit — c'est presque toujours la cause du refus.

⚠️ Le défaut (RequireAuthentication = false, AuthorizationPolicy = null) ne fait aucune vérification : toute personne capable d'atteindre /update peut déclencher la migration. Ce n'est acceptable que si l'accès à la page est déjà restreint par l'hôte. La garde journalise un avertissement au démarrage tant qu'aucune des deux options n'est configurée.

SignInPath est optionnel mais recommandé avec RequireAuthentication : sans lui, un visiteur anonyme est informé qu'il doit se connecter mais n'a aucun chemin pour le faire. Le chemin renseigné est automatiquement exempté de la garde (inutile de l'ajouter à ExemptPathPrefixes) et reçoit un paramètre ReturnUrl qui ramène à /update, laquelle conserve elle-même l'URL demandée à l'origine.

La politique est réévaluée au moment du clic, pas seulement à l'affichage.

Les confirmations sont sérialisées dans le processus : deux administrateurs qui cliquent en même temps ne lancent pas deux migrations concurrentes. En déploiement multi-instance, cette garantie ne suffit pas — EF Core ne pose un verrou côté base qu'à partir de la version 9, et dotnet ef database update reste la voie sûre.

Migrations hors EF Core

public sealed class DbUpMigrationManager : IMigrationManager
{
    public Task<IReadOnlyList<string>> GetPendingMigrationsAsync(CancellationToken ct = default) => ...;
    public Task ApplyPendingMigrationsAsync(CancellationToken ct = default) => ...;
}

builder.Services.AddDatabaseUpdateWith<DbUpMigrationManager>();

L'enregistrement d'IMigrationManager est idempotent : une implémentation déjà enregistrée par l'application n'est jamais supplantée.

Apparence

options.ShowIcon = false;                        // aucune icône
options.IconCssClass = "bi bi-database-gear";    // icône de fonte, si l'hôte la charge

Par défaut la page dessine sa propre icône SVG, sans dépendre d'aucune feuille de style externe.

Localisation

options.Texts porte tous les libellés de la page ; UpdateTexts.French (défaut) et UpdateTexts.English sont fournis, et le record se recopie au besoin :

options.Texts = UpdateTexts.English with { Continue = "Go to app" };

Licence

MIT — usage, modification, distribution et usage commercial libres, y compris dans un logiciel propriétaire. La seule obligation est de conserver la mention de copyright et le texte de la licence ; le logiciel est fourni sans garantie.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 99 9/12/2026