Condividi tramite


Comandi di risorse personalizzati in .NET.NET Aspire

Ogni risorsa nel modello di app .NET.NET Aspire è rappresentata come IResource e quando viene aggiunta al generatore di applicazioni distribuite , è il parametro di tipo generico dell'interfaccia IResourceBuilder<T>. È possibile usare il generatore di risorse API per concatenare le chiamate, configurare la risorsa sottostante e, in alcune situazioni, aggiungere comandi personalizzati alla risorsa. Alcuni scenari comuni per la creazione di un comando personalizzato potrebbero includere l'esecuzione di migrazioni di database o il popolamento/reimpostazione di un database. Questo articolo illustra come aggiungere un comando personalizzato a una risorsa Redis che cancella la cache.

Importante

Questi comandi .NET.NET Aspire del dashboard sono disponibili solo quando si esegue il dashboard localmente. Non sono disponibili quando si esegue il dashboard in Azure Container Apps.

Aggiungere comandi personalizzati a una risorsa

Per iniziare, creare una nuova .NET.NET Aspire app di avvio dai modelli disponibili . Per creare la soluzione da questo modello, seguire la Guida rapida : Costruisci la tua prima soluzione .NET.NET Aspire. Dopo aver creato questa soluzione, aggiungere una nuova classe denominata RedisResourceBuilderExtensions.cs al progetto host dell'app . Sostituire il contenuto del file con il codice seguente:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;

namespace Aspire.Hosting;

internal static class RedisResourceBuilderExtensions
{
    public static IResourceBuilder<RedisResource> WithClearCommand(
        this IResourceBuilder<RedisResource> builder)
    {
        builder.WithCommand(
            name: "clear-cache",
            displayName: "Clear Cache",
            executeCommand: context => OnRunClearCacheCommandAsync(builder, context),
            updateState: OnUpdateResourceState,
            iconName: "AnimalRabbitOff",
            iconVariant: IconVariant.Filled);

        return builder;
    }

    private static async Task<ExecuteCommandResult> OnRunClearCacheCommandAsync(
        IResourceBuilder<RedisResource> builder,
        ExecuteCommandContext context)
    {
        var connectionString = await builder.Resource.GetConnectionStringAsync() ??
            throw new InvalidOperationException(
                $"Unable to get the '{context.ResourceName}' connection string.");

        await using var connection = ConnectionMultiplexer.Connect(connectionString);

        var database = connection.GetDatabase();

        await database.ExecuteAsync("FLUSHALL");

        return CommandResults.Success();
    }

    private static ResourceCommandState OnUpdateResourceState(
        UpdateCommandStateContext context)
    {
        var logger = context.ServiceProvider.GetRequiredService<ILogger<Program>>();

        if (logger.IsEnabled(LogLevel.Information))
        {
            logger.LogInformation(
                "Updating resource state: {ResourceSnapshot}",
                context.ResourceSnapshot);
        }

        return context.ResourceSnapshot.HealthStatus is HealthStatus.Healthy
            ? ResourceCommandState.Enabled
            : ResourceCommandState.Disabled;
    }
}

Il codice precedente:

  • Condivide lo spazio dei nomi Aspire.Hosting affinché sia visibile al progetto host dell'app.
  • È un static class in modo che possa contenere metodi di estensione.
  • Definisce un singolo metodo di estensione denominato WithClearCommand, estendendo l'interfaccia IResourceBuilder<RedisResource>.
  • Il metodo WithClearCommand registra un comando denominato clear-cache che cancella la cache della risorsa Redis.
  • Il metodo WithClearCommand restituisce l'istanza di IResourceBuilder<RedisResource> per consentire il concatenamento.

L'API WithCommand aggiunge le annotazioni appropriate alla risorsa, che vengono utilizzate nel dashboard .NET.NET Aspire. Il dashboard usa queste annotazioni per visualizzare il comando nella UI. Prima di entrare troppo in questi dettagli, assicurarsi di comprendere prima i parametri del metodo WithCommand:

  • name: nome del comando da richiamare.
  • displayName: nome del comando da visualizzare nel dashboard.
  • executeCommand: il Func<ExecuteCommandContext, Task<ExecuteCommandResult>> da eseguire quando viene richiamato il comando, che è la posizione in cui viene implementata la logica del comando.
  • updateState: il callback Func<UpdateCommandStateContext, ResourceCommandState> viene richiamato per determinare lo stato "abilitato" del comando, che viene usato per abilitare o disabilitare il comando nel dashboard.
  • iconName: nome dell'icona da visualizzare nel dashboard. L'icona è facoltativa, ma quando la si fornisce, deve essere un nome valido dell'icona di Fluent UI Blazor.
  • iconVariant: la variante dell'icona da visualizzare nel dashboard, le opzioni valide sono Regular (impostazione predefinita) o Filled.

Esegui la logica del comando

Il delegato executeCommand è la posizione in cui viene implementata la logica dei comandi. Questo parametro è definito come Func<ExecuteCommandContext, Task<ExecuteCommandResult>>. Il ExecuteCommandContext fornisce le proprietà seguenti:

  • ExecuteCommandContext.ServiceProvider: L'istanza di IServiceProvider usata per risolvere i servizi.
  • ExecuteCommandContext.ResourceName: nome dell'istanza della risorsa in cui viene eseguito il comando.
  • ExecuteCommandContext.CancellationToken: il CancellationToken usato per annullare l'esecuzione del comando.

Nell'esempio precedente il delegato executeCommand viene implementato come metodo async che cancella la cache della risorsa Redis. Delega a una funzione privata di ambito classe denominata OnRunClearCacheCommandAsync per eseguire la cancellazione effettiva della cache. Si consideri il codice seguente:

private static async Task<ExecuteCommandResult> OnRunClearCacheCommandAsync(
    IResourceBuilder<RedisResource> builder,
    ExecuteCommandContext context)
{
    var connectionString = await builder.Resource.GetConnectionStringAsync() ??
        throw new InvalidOperationException(
            $"Unable to get the '{context.ResourceName}' connection string.");

    await using var connection = ConnectionMultiplexer.Connect(connectionString);

    var database = connection.GetDatabase();

    await database.ExecuteAsync("FLUSHALL");

    return CommandResults.Success();
}

Il codice precedente:

  • Recupera la stringa di connessione dalla risorsa Redis.
  • Si connette all'istanza di Redis.
  • Ottiene l'istanza del database.
  • Esegue il comando FLUSHALL per cancellare la cache.
  • Restituisce un'istanza di CommandResults.Success() per indicare che il comando ha avuto esito positivo.

Aggiornare la logica dello stato del comando

Il delegato updateState è il punto in cui viene determinato lo stato del comando. Questo parametro è definito come Func<UpdateCommandStateContext, ResourceCommandState>. Il UpdateCommandStateContext fornisce le proprietà seguenti:

  • UpdateCommandStateContext.ServiceProvider: L'istanza di IServiceProvider usata per risolvere i servizi.
  • UpdateCommandStateContext.ResourceSnapshot: snapshot dell'istanza della risorsa in cui viene eseguito il comando.

Lo snapshot non modificabile è un'istanza di CustomResourceSnapshot, che espone tutti i tipi di dettagli importanti sull'istanza della risorsa. Si consideri il codice seguente:

private static ResourceCommandState OnUpdateResourceState(
    UpdateCommandStateContext context)
{
    var logger = context.ServiceProvider.GetRequiredService<ILogger<Program>>();

    if (logger.IsEnabled(LogLevel.Information))
    {
        logger.LogInformation(
            "Updating resource state: {ResourceSnapshot}",
            context.ResourceSnapshot);
    }

    return context.ResourceSnapshot.HealthStatus is HealthStatus.Healthy
        ? ResourceCommandState.Enabled
        : ResourceCommandState.Disabled;
}

Il codice precedente:

  • Recupera l'istanza del logger dal provider di servizi.
  • Registra i dettagli dello snapshot della risorsa.
  • Restituisce ResourceCommandState.Enabled se la risorsa è funzionante; in caso contrario, restituisce ResourceCommandState.Disabled.

Testare il comando personalizzato

Per testare il comando personalizzato, aggiornare il file di Program.cs del progetto host dell'app per includere il codice seguente:

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache")
                   .WithClearCommand();

var apiService = builder.AddProject<Projects.AspireApp_ApiService>("apiservice");

builder.AddProject<Projects.AspireApp_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(cache)
    .WaitFor(cache)
    .WithReference(apiService)
    .WaitFor(apiService);

builder.Build().Run();

Il codice precedente chiama il metodo di estensione WithClearCommand per aggiungere il comando personalizzato alla risorsa Redis. Avvia l'app e vai al dashboard .NET.NET Aspire. Il comando personalizzato dovrebbe essere elencato sotto la risorsa Redis. Nella pagina Risorse del dashboard, selezionare il pulsante con i puntini di sospensione nella colonna Azioni:

.NET Aspire dashboard: Redis risorsa cache con comando personalizzato visualizzato.

L'immagine precedente mostra il comando cancella cache aggiunto alla risorsa Redis. L'icona viene visualizzata come un coniglio barrato per indicare che la velocità della risorsa dipendente viene azzerata.

Selezionare il comando Cancella cache per cancellare la cache della risorsa Redis. Il comando deve essere eseguito correttamente e la cache deve essere cancellata:

.NET Aspire dashboard: Redis risorsa cache con comando personalizzato eseguito.

Vedere anche