.NET Aspire 2: le tableau de bord du développeur

Cet article montre comment créer un tableau de bord et l’intégrer avec .NET Aspire.
Ceci est la deuxième partie d’une série exploratoire en cinq parties sur .NET Aspire.
- Partie 1: Qu’est-ce que .NET Aspire?
- Ce message (partie 2): Explorer le tableau de bord du développeur
- Partie 3: défaut et intégrations intelligentes
- Partie 4: découverte d’orchestration et de service
- Partie 5: Déploiement à l’aide d’applications de conteneurs Azure
Bonjour encore, amis et bienvenue. Si vous avez manqué le Intro à environ .net Aspiren’hésitez pas à vérifier cela avant de lire le reste de cette pièce. Aujourd’hui, nous nous concentrerons sur ma fonctionnalité préférée: le tableau de bord du développeur. Le tableau de bord vous permet de suivre votre application en temps réel via une seule interface utilisateur.
Avant d’y arriver, je veux d’abord vous guider à travers le projet que nous utiliserons: la boutique de guitare de Dave. Si vous vous souvenez, voici l’architecture initiale de haut niveau de ma solution sans aspire.
Explorer le projet
Pour ce post, nous nous concentrerons sur le /inventory
API. Cette API est consommée par une application Web côté serveur Blazor. Je vais vous guider parties clés de la solution.
Le code API
Nous utilisons une API minimale rapide ASP.NET avec des opérations CRUD (créer, lire, mettre à jour, supprimer). Les données sont persistées en un serveur SQL conteneurisé et utilisent l’entité Framework pour la capacité de requête.
Voici un échantillon des appels API:
app.MapGet("/guitars", async (GuitarStoreContext db) =>
{
var list = await Queries.GetAllGuitars(db).ToListAsync();
return Results.Ok(list);
});
app.MapGet("/guitars/{id:guid}", async (Guid id, GuitarStoreContext db) =>
await Queries.GetGuitarById(db, id) is { } dto
? Results.Ok(dto)
: Results.NotFound());
app.MapPost("/guitars", async (Guitar guitar, GuitarStoreContext db) =>
{
db.Guitars.Add(guitar);
await db.SaveChangesAsync();
return Results.Created($"/guitars/{guitar.Id}", guitar);
});
app.MapPut("/guitars/{id:guid}", async (Guid id, Guitar updated, GuitarStoreContext db) =>
{
var guitar = await db.Guitars.FindAsync(id);
if (guitar is null) return Results.NotFound();
db.Entry(guitar).CurrentValues.SetValues(updated);
await db.SaveChangesAsync();
return Results.NoContent();
});
app.MapDelete("/guitars/{id:guid}", async (Guid id, GuitarStoreContext db) =>
{
var guitar = await db.Guitars.FindAsync(id);
if (guitar is null) return Results.NotFound();
db.Guitars.Remove(guitar);
await db.SaveChangesAsync();
return Results.NoContent();
});
La base de données
Du côté SQL, mon Dockerfile
utilise le mcr.microsoft.com/mssql/server:2022-latest
image. Si vous êtes intéressé à en savoir plus, Consultez la documentation Microsoft. À l’heure actuelle, j’utilise Docker Compose pour gérer le conteneur.
Dans le prochain article, nous discuterons de la façon de simplifier cela avec .net Aspire.
L’application Blazor
Enfin, cette API est consommée par une application Web Blazor. Pour l’instant, nous montrerons le composant principal où nous chargeons toutes les guitares de l’inventaire.
@page "https://www.telerik.com/"
@inject BackendHttpClient HttpClient
<PageTitle>Guitars</PageTitle>
<div class="container py-4">
<h1 class="display-6 mb-4 d-flex align-items-center gap-2">
<span>🎸</span> Guitar Catalog
</h1>
@if (_guitars is null)
{
<div class="d-flex align-items-center" aria-label="Loading">
<div class="spinner-border text-primary me-2" role="status">
<span class="visually-hidden">Loading…</span>
</div>
<span>Loading guitars…</span>
</div>
}
else if (!_guitars.Any())
{
<p class="text-muted">No guitars found.</p>
}
else
{
<div class="card shadow-sm">
<div class="card-header bg-primary text-white fw-semibold">
Guitar List
</div>
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle">
<thead class="table-light">
<tr class="small text-uppercase">
<th scope="col">Brand</th>
<th scope="col">Model</th>
<th scope="col">Series</th>
<th scope="col" class="text-end">Price</th>
</tr>
</thead>
<tbody>
@foreach (var g in _guitars!)
{
<tr>
<td>@g.Brand</td>
<td>@g.Model</td>
<td>@g.Series</td>
<td class="text-end">@g.Price.ToString("C")</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
</div>
@code {
private List<GuitarDto>? _guitars;
protected override async Task OnInitializedAsync()
{
_guitars = await HttpClient.GetGuitarsAsync();
}
}
Et avec cela, nous avons une table qui répertorie tout en inventaire.
Ajout du tableau de bord
Maintenant que nous avons une application, ajoutons-y une observabilité en utilisant le tableau de bord .net Aspire.
Quel tableau de bord choisir?
Nous avons deux choix: en utilisant mode autonome ou Un projet .net Aspire.
Avec le mode autonome, vous pouvez tirer une image Docker et l’utiliser sans le reste de .NET Aspire. Supposons que votre équipe d’opérations ait besoin d’un «volet unique de verre» pour tous vos services – selon la moitié de vos services, et le reste est en nœud ou en python.
Un seul tableau de bord peut ingérer le trafic de tout le monde (OTLP) sans se soucier de qui a écrit le code. De plus, il peut fonctionner comme son propre conteneur ou déploiement et rester en place même si votre application se bloque.
Nous utiliserons le tableau de bord Aspire intégré. Cela brille pendant notre processus de boucle intérieure: modifiez le code, appuyez sur F5 dans Visual Studio et regardez les traces. De cette façon, le tableau de bord apparaît automatiquement et est câblé à chaque conteneur, projet et secret que nous connaissons.
Dans cet esprit, nous commencerons à aspirer notre projet.
Ajouter Aspire à notre application existante
Il existe de nombreuses façons d’ajouter Aspire à une application existante; Pour nous, nous le ferons via des outils Visual Studio.
Dans Visual Studio, créons un nouveau projet et recherchons le Hôte d’application .net aspire modèle.
Une fois que vous avez ajouté le projet, vous verrez deux nouveaux projets ajoutés à Solution Explorer: un projet AppHost et un projet de bibliothèque ServiceFaults. Parlons du projet AppHost (nous couvrirons en détail ServiceFaults dans notre prochain article).
Le projet AppHost
Nous pouvons considérer AppHost comme un centre de commande pour notre application. Au lieu de jongler avec un tas de configurations, Docker Compose Files and Scripts, ce projet a un seul profil de lancement qui démarre notre application et ses dépendances avec accès à un tableau de bord entièrement câblé.
Par défaut, le fichier comprend ces deux lignes qui emballent vraiment un punch:
var builder = DistributedApplication.CreateBuilder(args);
builder.Build().Run();
Ce code analyse chaque projet ou un conteneur référencé, construit un graphique de dépendance, injecte des variables d’environnement (comme les chaînes de connexion et les secrets) et lance le tableau de bord .NET Aspire.
Comment tout cela fonctionne-t-il?
- AppHost commence.
- Pour chaque projet, il s’exécute avec une commande de lancement générée.
- Il fonctionne
docker run
Dans les coulisses de chaque conteneur. - Il définit des variables d’environnement comme
Api__BaseAddress
etConnectionStrings__DefaultConnection
. - Les par défaut du service (à discuter la prochaine fois!) Découvrez le point de terminaison du tableau de bord à
OTEL_EXPORTER_OTLP_ENDPOINT
. - Vous avez un tableau de bord glorieux à utiliser hors de la boîte.
Avant de lancer l’application et de le voir en action, ajoutons quelques lignes de code à notre AppHost
projet.
var builder = DistributedApplication.CreateBuilder(args);
var api = builder.AddProject<Projects.Api>("api");
builder.AddProject<Projects.Frontend>("frontend")
.WithReference(api)
.WaitFor(api)
.WithExternalHttpEndpoints();
builder.Build().Run();
À partir d’un haut niveau, voici ce qui se passe:
DistributedApplication.CreateBuilder(args)
Crée un constructeur qui gérera tous les services, conteneurs et valeur de configuration.AddProject<Projects.Api>("api")
dit au constructeur de tourner le conteneur API et de le faire adressable commeapi
pour que d’autres services se référent.- Pour le frontend,
AddProject<Projects.Frontend>("frontend")
enregistre l’interface utilisateur etWithReference(api)
tend l’interface utilisateur L’URL de base de l’API. Alors,WaitFor(api)
fait attendre l’interface utilisateur que l’API soit saine. Enfin,WithExternalHttpEndpoints()
expose l’interface utilisateur pour qu’un navigateur puisse l’atteindre.
Il y a beaucoup, beaucoup plus que nous pouvons (et le feront!) Faire ici, mais pour l’instant, cela nous aidera à «connecter» l’interface utilisateur avec l’API dans notre tableau de bord.
Assez déjà! Voyons-le.
Le tableau de bord en action
Tout de suite, nous voyons une interface unifiée pour notre application. Ici, vous pouvez facilement accéder à nos services et accéder rapidement aux journaux, traces et métriques au même endroit.
Si nous cliquons sur la section Console, vous pouvez sélectionner parmi les api
ou frontend
journaux. Plus de bascule douloureuse entre plusieurs consoles!
Nous avons une journalisation structurée qui nous permet de filtrer facilement ce dont nous avons besoin, comme une commande SQL:
Avec des traces, nous pouvons parcourir nos dépendances et combien de temps ils prennent. Pour l’instant, nous pouvons voir les détails de la demande HTTP et de l’appel SQL.
Nous avons également des mesures pré-cuites et un graphique de dépendance (qui deviendra plus intéressant sur cette série, je le promets).
Si vous cliquez api
vous pouvez obtenir des informations sur les contrôles de santé et les variables environnementales utilisées par Aspire.
Il y a beaucoup plus de fonctionnalités que je n’ai pas d’espace à couvrir, comme: Authentification du tableau de bord, métrique et Intégration de copilote github. Je suggérerais de construire un nouveau projet Aspire et de cliquer pour vous-même. Je ne pense pas que je puisse revenir au débogage et au traçage locaux avant Aspire.
Emballage
J’espère que vous avez apprécié en savoir plus sur les capacités .NET Aspire Doashboard. La prochaine fois, nous travaillerons sur les intégrations et les valeurs par défaut intelligentes, où nous pouvons ajouter notre conteneur SQL Server avec seulement quelques lignes de code.
Rendez-vous alors, et heureux codage!
Source link