Compartir a través de


Comandos de recursos personalizados en .NET.NET Aspire

Cada recurso del modelo de aplicación .NET.NET Aspire se representa como un IResource y, cuando se agrega al generador de aplicaciones distribuido , es el parámetro de tipo genérico de la interfaz IResourceBuilder<T>. Usas la API de constructor de recursos para encadenar llamadas, configurar el recurso subyacente, y en algunas situaciones, puedes añadir comandos personalizados al recurso. Algunos escenarios comunes para crear un comando personalizado incluirían ejecutar migraciones de base de datos o sembrar/restablecer una base de datos. En este artículo, aprenderá a agregar un comando personalizado a un recurso de Redis que borra la memoria caché.

Importante

Estos comandos .NET.NET Aspire del tablero de control solo están disponibles cuando el tablero de control se ejecuta localmente. No están disponibles al ejecutar el panel en Azure Container Apps.

Adición de comandos personalizados a un recurso

Empiece por crear una nueva aplicación de inicio de .NET.NET Aspire a partir de las plantillas disponibles . Para crear la solución a partir de esta plantilla, siga el inicio rápido de : Compilación de la primera solución de .NET.NET Aspire. Después de crear esta solución, agregue una nueva clase denominada RedisResourceBuilderExtensions.cs al proyecto host de aplicación de . Reemplace el contenido del archivo por el código siguiente:

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;
    }
}

El código anterior:

  • Comparte el espacio de nombres Aspire.Hosting para que el proyecto anfitrión de la aplicación pueda verlo.
  • Es un static class para que pueda contener métodos de extensión.
  • Define un único método de extensión denominado WithClearCommand, ampliando la interfaz IResourceBuilder<RedisResource>.
  • El método WithClearCommand registra un comando denominado clear-cache que borra la memoria caché del recurso Redis.
  • El método WithClearCommand devuelve la instancia de IResourceBuilder<RedisResource> para permitir el encadenamiento.

La API de WithCommand agrega las anotaciones adecuadas al recurso, que se consumen dentro del panel de .NET.NET Aspire. El panel usa estas anotaciones para representar el comando en la interfaz de usuario. Antes de profundizar demasiado en esos detalles, asegúrese de que primero comprende los parámetros del método WithCommand:

  • name: el nombre del comando que se va a invocar.
  • displayName: el nombre del comando que se va a mostrar en el panel.
  • executeCommand: el Func<ExecuteCommandContext, Task<ExecuteCommandResult>> que se va a ejecutar cuando se invoca el comando, que es donde se implementa la lógica de comandos.
  • updateState: se invoca la devolución de llamada Func<UpdateCommandStateContext, ResourceCommandState> para determinar el estado "habilitado" del comando, que se usa para habilitar o deshabilitar el comando en el panel.
  • iconName: el nombre del icono que se va a mostrar en el panel. El icono es opcional, pero cuando lo proporciones, debe ser un nombre de icono válido de la interfaz de usuario Fluent Blazor.
  • iconVariant: la variante del icono que se va a mostrar en el panel, las opciones válidas se Regular (valor predeterminado) o Filled.

Ejecutar la lógica del comando

El delegado executeCommand es donde se implementa la lógica de comandos. Este parámetro se define como un Func<ExecuteCommandContext, Task<ExecuteCommandResult>>. El ExecuteCommandContext proporciona las siguientes propiedades:

  • ExecuteCommandContext.ServiceProvider: la instancia de IServiceProvider que se usa para resolver los servicios.
  • ExecuteCommandContext.ResourceName: el nombre de la instancia de recurso en la que se ejecuta el comando.
  • ExecuteCommandContext.CancellationToken: el CancellationToken que se usa para cancelar la ejecución del comando.

En el ejemplo anterior, el delegado executeCommand se implementa como un método async que borra la memoria caché del recurso de Redis. Se delega a una función privada de ámbito de clase llamada OnRunClearCacheCommandAsync para realizar la limpieza real de la caché. Tenga en cuenta el código siguiente:

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();
}

El código anterior:

  • Recupera la cadena de conexión del recurso Redis.
  • Se conecta a la instancia de Redis.
  • Obtiene la instancia de base de datos.
  • Ejecuta el comando FLUSHALL para borrar la memoria caché.
  • Devuelve una instancia de CommandResults.Success() para indicar que el comando se realizó correctamente.

Actualización de la lógica de estado del comando

El delegado updateState es donde se determina el estado del comando. Este parámetro se define como un Func<UpdateCommandStateContext, ResourceCommandState>. El UpdateCommandStateContext proporciona las siguientes propiedades:

  • UpdateCommandStateContext.ServiceProvider: la instancia de IServiceProvider que se usa para resolver los servicios.
  • UpdateCommandStateContext.ResourceSnapshot: la instantánea de la instancia de recurso en la que se ejecuta el comando.

La instantánea inmutable es una instancia de CustomResourceSnapshot, que expone una variedad de detalles valiosos sobre la instancia de recurso. Tenga en cuenta el código siguiente:

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;
}

El código anterior:

  • Recupera la instancia del registrador del proveedor de servicios.
  • Registra los detalles de la instantánea de recursos.
  • Devuelve ResourceCommandState.Enabled si el recurso está en buen estado; de lo contrario, devuelve ResourceCommandState.Disabled.

Prueba del comando personalizado

Para probar el comando personalizado, actualice el archivo Program.cs del proyecto host de la aplicación para incluir el código siguiente:

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();

El código anterior llama al método de extensión WithClearCommand para agregar el comando personalizado al recurso Redis. Ejecute la aplicación y vaya al panel de .NET.NET Aspire. Debería ver el comando personalizado que aparece en el recurso Redis. En la página Recursos del panel de control, seleccione el botón de elipsis en la columna Acciones:

.NET Aspire tablero: Redis recurso de caché con el comando personalizado mostrado.

En la imagen anterior se muestra el comando clear cache que se agregó al recurso de . El icono se muestra como un conejo cruza para indicar que se borra la velocidad del recurso dependiente.

Seleccione el comando Borrar caché para borrar la memoria caché del recurso de Redis. El comando debe ejecutarse correctamente y la memoria caché debe borrarse:

.NET Aspire tablero: Redis recurso de caché con el comando personalizado ejecutado.

Consulte también