Fermer

août 28, 2025

.NET Aspire 4: intégrations

.NET Aspire 4: intégrations


La série .NET Aspire se poursuit avec les intégrations. Nous ajouterons des intégrations SQL Server et Redis et montrerons comment Aspire le gère pour nous.

Il s’agit de la quatrième partie d’une série exploratoire en six parties sur .NET Aspire.

Bonjour encore, amis.

Dans les trois premiers versements de cette série .net Aspire, nous avons fait ce qui suit:

  • Dit Salut à .net Aspire et a vu comment son approche d’opinion supprime une tonne de douleur native du nuage.
  • Tiré le tableau de bord du développeur Pour regarder les traces, les journaux et les mesures sans jongler avec les fenêtres terminales.
  • Rencontré Service par défaut et a appris comment une méthode d’extension unique répartit la télémétrie, les contrôles de santé et la résilience dans tous les projets.

Aujourd’hui, nous transformerons l’infrastructure en code C #. Nous allons saisir mon conteneur SQL Server piraté à partir de Docker Compose, le déposer dans Aspire et accélérer les lectures avec un cache Redis. Les deux ressources tournent, câblent et s’allument dans le tableau de bord. Aucune gymnastique portuaire n’est requise.

Assez de parler! Apporter le code.

Ajouter un serveur SQL conteneurisé à .net Aspire

Mon conteneur SQL Server fonctionne dans Docker Compose mais c’est un citoyen de deuxième classe. En l’invitant à aspirer, nous obtenons:

  • Une chaîne d’outils partagée: Nous en utilisons un AppHostun profil de lancement et un pipeline CI.
  • Pas extra docker compose up étape: Frapper F5 (ou dotnet run) et SQL commence par tout le reste.
  • Sondes de santé automatiques, découvertes et secrets: Le service par défaut injecte les chaînes de connexion et les points de terminaison OTLP pour nous.
  • Observabilité native: Les journaux, les traces et les métriques se déroulent directement vers le tableau de bord du développeur.

Ajouter SQL Server en tant que ressource Aspire

Pour commencer cette fête, naviguez vers notre AppHost Projet et mise à jour Program.cs. Après avoir examiné le code, nous le parcourons en détail.

var builder = DistributedApplication.CreateBuilder(args);

var password = builder.AddParameter("password", secret: true);
var server = builder.AddSqlServer("server", password, 1433)
        .WithDataVolume("guitar-data")
        .WithLifetime(ContainerLifetime.Persistent);

var db = server.AddDatabase("guitardb");

var api = builder.AddProject<Projects.Api>("api")
    .WithReference(db)
    .WaitFor(db);


builder.AddProject<Projects.Frontend>("frontend")
    .WithReference(api)
    .WaitFor(api)
    .WithExternalHttpEndpoints();

builder.Build().Run();

Capture en toute sécurité des secrets

Dans cet exemple:

var password = builder.AddParameter("password", secret: true);

AddParameter Crée une ressource de paramètre. Lorsque nous définissons le secret paramètre truenous disons à Aspire de traiter la valeur comme un secret et facilite la remplacement avec une variable d’environnement ou des secrets d’utilisateurs pendant le développement local.

Dans notre secrets.json (ou appsettings.json), Je l’ai lu en utilisant la structure suivante:

{
  "Parameters": {
    "password": "Dave#123"
  }
}

Évidemment, Cela ne convient que pour le développement local. Lors du déploiement sur des serveurs, vous voudrez un meilleur modèle de sécurité comme Azure Key Vault. Dans ce cas, vous pouvez mettre à jour votre code à ce qui suit:

var keyVault = builder.AddAzureKeyVault("key‑vault");
builder.Configuration.AddAzureKeyVaultSecrets("key‑vault");   

var sqlPassword = builder.AddParameter("sql-password", secret: true);

Tournez un serveur SQL avec stockage

Pour cet exemple:

var server = builder.AddSqlServer("server", password, 1433)
        .WithDataVolume("guitar-data")
        .WithLifetime(ContainerLifetime.Persistent);

Que vient-il de se passer?

  • AddSqlServer tire le fonctionnaire mcr.microsoft.com/mssql/server:2022-latest image, sets SA_PASSWORD à notre mot de passe et le mappe au port 1433.
  • WithDataVolume("guitar-data") Monte un volume Docker nommé pour que notre conteneur survit des reconstructions.
  • Enfin, WithLifetime(ContainerLifetime.Persistent) dit à Aspire de réutiliser la même instance entre les courses. Cela évite les temps de démarrage lents et devoir exécuter des migrations à chaque fois.

Ajout de la base de données

Ce liner…

var db = server.AddDatabase("guitardb");

… Le sucre syntaxtique est-il pour écrire un CREATE DATABASE guitardb Commande lorsque nous lançons pour la première fois, et génère également une ressource de chaîne de connexion fortement tapée. Cette chaîne de connexion peut désormais être référencée par n’importe quel projet qui en a besoin.

Nous avons ajouté la base de données, mais elle est vide et nous devons la semer. En commençant par Aspire 9.2, vous pouvez utiliser le WithCreationScript méthode.

Avec un script (ou un ensemble de scripts) en main, assurez-vous de le copier dans le AppHost Répertoire de sortie en l’ajoutant à votre fichier de projet:

<ItemGroup>
  <None Include="..\Api\data\createdb.sql" Link="createdb.sql">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

Nous pouvons maintenant mettre à jour les apphost Program.cs Code de base de données à ce qui suit.

var password = builder.AddParameter("password", secret: true);
var server = builder.AddSqlServer("server", password, 1433)
        .WithDataVolume("guitar-data")
        .WithLifetime(ContainerLifetime.Persistent);

var scriptPath = Path.Join(Path.GetDirectoryName(typeof(Program).Assembly.Location), "createdb.sql");
var db = server.AddDatabase("guitardb").WithCreationScript(File.ReadAllText(scriptPath));

Ce n’est qu’une des nombreuses façons de voir les données. Pour en savoir plus, Lire les documents Microsoft.

Enregistrez l’API avec la base de données

Enfin, ce code enregistre l’API à la base de données.

var api = builder.AddProject<Projects.Api>("api")
    .WithReference(db)
    .WaitFor(db);

Le WithReference(db) Injecte deux choses dans le conteneur:

  • La chaîne de connexion comme ConnectionStrings__guitardb
  • L’adresse de la santé de la santé du conteneur SQL afin que votre application sache quand la base de données est en place

Le WaitFor(db) Ligne signifie que Aspire ne sera même pas docker run L’API jusqu’à SQL Server le signale comme saine.

Mettez à jour l’API d’inventaire

Parce que nous comptons déjà sur les valeurs par défaut du service, l’API peut ramasser builder.Configuration.GetConnectionString("db") automatiquement. Le seul code que nous devons toucher est de s’assurer que EF Core est enregistré pour utiliser la base de données dans le projet API Program.cs:

builder.Services.AddSqlServer<GuitarStoreContext>("guitardb");

Cette seule ligne permet au AppHost Projet pour faire tourner le conteneur SQL Server et injecter sa chaîne de connexion dans l’API au lancement. Le service par défaut est ensuite de la journalisation, du traçage, des métriques et de la résilience. En conséquence, le AddSqlServer<GuitarStoreContext>("guitardb") L’appel saisit la chaîne de connexion injectée, enregistre le DbContext et se branche sur notre observabilité hors de la boîte.

Examiner le tableau de bord

Lorsque nous lançons l’application, nous voyons maintenant la base de données opérationnelle à côté de notre application.

Tableau de bord avec SQL

Si nous passons en revue les détails de la ressource API, nous voyons également la chaîne de connexion injectée.

Variable DB ENV

Notre télémétrie s’allume: si nous naviguons vers nos traces, nous pouvons voir une demande de bout en bout.

trace de base de données

Notre base de données est officiellement hors de Docker Compose et dans Aspire! Nous pouvons maintenant supprimer l’ancien docker-compose.yml et utilisez SQL Server aux côtés du reste de notre projet avec une seule commande F5 (ou dotnet run).

Nous pouvons maintenant passer à notre intégration Redis pour accélérer nos lectures.

Ajout de redis pour la mise en cache rapide de la foudre

Notre /guitars Le point de terminaison est lourd en lecture. Pour le rendre rapide à la foudre (et pour éviter lestiné à la base de données), nous ajouterons Redis en tant que couche de cache et permettons d’aspirer les choses.

Pour filmer Redis dans AppHostnous allons l’ajouter juste après l’extrait SQL.

var cache = builder.AddRedis("cache")
                   .WithRedisInsight()
                   .WithLifetime(ContainerLifetime.Persistent);

Que se passe-t-il ici? Nous tirons un conteneur redis de docker.io/library/redis Cela aide à éviter les installations locales ou les cordes de port locales. Nous obtenons également un PING sonde de santé qui est visible à partir du /health Point de terminaison et tableau de bord du développeur.

Ensuite, la chaîne de connexion ConnectionStrings:cache est injecté automatiquement dans tout projet qui appelle WithReference(cache).

Facultativement, nous pouvons également inclure notre interface utilisateur d’administration Redis préférée, en tant que supports Aspire Redis insight ou Redis commandant. J’ai ajouté Redis Insight. Quand je le fais, Aspire s’empare docker.io/redis/redisinsight pour me permettre de travailler facilement avec mon instance redis.

Tout comme avec notre conteneur SQL Server, nous ajoutons .WithLifetime(ContainerLifetime.Persistent) Pour dire Aspire, « Ne démolir pas le conteneur quand j’arrête de déboguer. » Le résultat est des start-ups beaucoup plus rapides et un état durable entre les sessions – le grand pour le développement quotidien, les démos ou les antécédents de longue durée. Si jamais vous avez besoin d’une ardoise propre, vous pouvez toujours tailler le conteneur manuellement ou ramener la durée de vie à la valeur par défaut ContainerLifetime.Transient mode.

Voici mon tout Program.cs dans AppHost:

var builder = DistributedApplication.CreateBuilder(args);

var password = builder.AddParameter("password", secret: true);
var server = builder.AddSqlServer("server", password, 1433)
        .WithDataVolume("guitar-data")
        .WithLifetime(ContainerLifetime.Persistent);

var db = server.AddDatabase("guitardb");

var cache = builder.AddRedis("cache")
            .WithRedisInsight()
            .WithLifetime(ContainerLifetime.Persistent);

var api = builder.AddProject<Projects.Api>("api")
    .WithReference(db)
    .WithReference(cache)
    .WaitFor(db);

builder.AddProject<Projects.Frontend>("frontend")
    .WithReference(api)
    .WaitFor(api)
    .WithExternalHttpEndpoints();

builder.Build().Run();

Mettez à jour notre API pour utiliser Redis

Comment mettre à jour l’API pour utiliser Redis? Sauprouillons une mise en cache minimale sur le GET /guitars point de terminaison. Après avoir installé le StackExchange.Redis Emballage, nous pouvons ajouter un décorateur pour utiliser la mise en cache de sortie.

app.UseOutputCache();

app.MapGet("/guitars",
   [OutputCache(Duration = 30)] async (GuitarStoreContext db) =>
       await db.Guitars
               .OrderBy(g => g.Brand).ThenBy(g => g.Model)
               .Select(g => new { g.Id, g.Brand, g.Model, g.Series, g.Price, g.SalePrice })
               .ToListAsync());

Le middleware de mise en cache de sortie peut désormais intercepter les demandes après que Aspire ait enregistré le magasin Redis. Pour les tests, nous utilisons un TTL de 30 secondes, mais une fois que nous sommes en ligne, nous pouvons l’ajuster par la configuration.

Exécutons maintenant la solution et ouvrons le tableau de bord. Nos ressources continuent de se développer.

Tableau de bord avec cache

Si nous naviguons sur notre URL Redis Insight, nous pouvons voir le cache!

redis insight

Lorsque nous licencions certains appels, nous voyons maintenant une demande de bout en bout terminer en moins de 3,43 ms. Pas mal!

traces redis

Avec SQL Server et Redis dans Play, notre tableau de bord montre désormais un graphique de dépendance pointant vers les nœuds SQL et Redis.

graphique de dépendance

Les développeurs sont heureux

Avant Aspire (et Docker Containeurs), pouvez-vous imaginer tout le travail, un nouveau développeur accès à un environnement local avec plusieurs API, la journalisation, un serveur SQL et une instance Redis?

Maintenant, voici tout ce que nous devons faire:

git clone https://github.com/davesguitarshop
cd inventory
dotnet run --project GuitarShop.AppHost

Avec une seule commande, nous restaurons les packages NuGet, créons des projets, démarrons nos conteneurs SQL Server et Redis, lancez notre tableau de bord et ouvrez un onglet de navigateur à https://localhost:5001. Pas d’installations SQL, non docker compose up Et pas de «quels ports devons-nous utiliser à nouveau?» Quel monde.

Un aperçu: arriver au nuage

Bien que le bonheur du développeur soit énorme, le véritable gain survient lorsque nous poussons vers le cloud – aux applications de conteneurs Azure, par exemple. Parce que la même chose AppHost La définition voyage avec nous, le pipeline n’a besoin que:

az containerapp up \
  --name guitar-shop \
  --resource-group guitar-shop-rg \
  --subscription guitar-shop-prd

En conséquence, l’extension de l’application Azure Container d’Aspire sera mapée:

  • AddSqlServer – Azure SQL (ou apporter votre propre image)
  • AddRedis – Azure Cache pour redis (ou votre propre image)
  • Problèmes de santé – Points de terminaison de la vivacité et de préparation de style Kubernetes
  • OTLP – Azure Monitor / AppInSights

Nous aurons beaucoup plus dans la partie 6 – assis à l’écoute!

Envelopper (et quelle est la prochaine)

Nous venons de remplacer un énorme docker-compose.ymlune gymnastique à demi-radieuse à l’environnement et un réadme plein de «Run This Before This» commandes avec seulement quelques lignes de C # – toutes découvrables de Intellisense. N’êtes-vous pas diverti?

Les principaux plats à retenir:

  • Les intégrations sont des ressources, pas des tâches. Après avoir déclaré l’intention avec AddSqlServer et AddRedisAspire gère les images, les ports et la santé.
  • Les chaînes de connexion s’écoulent automatiquement.
  • L’observabilité est intégrée. Chaque requête, hit du cache et exception apparaissent dans le tableau de bord avec zéro code supplémentaire.
  • La configuration locale des développeurs est triviale. Les nouveaux développeurs exécutent une commande et commencent le codage.
  • Portabilité. La même définition AppHost se déplace sur Azure Container Apps, AKS ou Docker Swarm avec un minimum de réglages.

Dans la partie 5, nous allons plonger dans la découverte et l’orchestration des services – comment Aspire permet aux services de se parler sans URL du codage dur et comment les politiques Polly maintiennent les choses résilientes lorsque le réseau se fâche.

Jusque-là, codage heureux et à bientôt!




Source link