Partilhar via


Comandos de recursos personalizados no .NET.NET Aspire

Cada recurso no modelo de aplicativo .NET.NET Aspire é representado como um IResource e, quando adicionado ao do construtor de aplicativos distribuído, é o parâmetro de tipo genérico da interface IResourceBuilder<T>. Use o resource builder API para encadear chamadas, configurar o recurso subjacente e, em algumas situações, talvez queira adicionar comandos personalizados ao recurso. Algum cenário comum para criar um comando personalizado pode ser a execução de migrações de banco de dados ou a propagação/redefinição de um banco de dados. Neste artigo, você aprenderá a adicionar um comando personalizado a um recurso de Redis que limpa o cache.

Importante

Esses .NET.NET Aspire comandos de do painel só estão disponíveis ao executar o painel localmente. Eles não estão disponíveis ao executar o painel no Azure Container Apps.

Adicionar comandos personalizados a um recurso

Comece por criar uma nova aplicação inicial .NET.NET Aspire a partir dos modelos disponíveis. Para criar a solução a partir deste modelo, siga o Guia de início rápido: crie sua primeira solução .NET.NET Aspire. Depois de criar essa solução, adicione uma nova classe chamada RedisResourceBuilderExtensions.cs ao projeto de host do aplicativo . Substitua o conteúdo do arquivo pelo seguinte código:

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

O código anterior:

  • Compartilha o namespace Aspire.Hosting para que ele fique visível para o projeto de host do aplicativo.
  • É um static class para que possa conter métodos de extensão.
  • Ele define um único método de extensão chamado WithClearCommand, estendendo a interface IResourceBuilder<RedisResource>.
  • O método WithClearCommand registra um comando chamado clear-cache que limpa o cache do recurso Redis.
  • O método WithClearCommand retorna a instância IResourceBuilder<RedisResource> para permitir o encadeamento.

A API WithCommand adiciona as anotações apropriadas ao recurso, que são consumidas no dashboard .NET.NET Aspire. O painel usa essas anotações para renderizar o comando na interface do usuário. Antes de ir muito longe nesses detalhes, vamos garantir que você primeiro entenda os parâmetros do método WithCommand:

  • name: O nome do comando a ser invocado.
  • displayName: O nome do comando a ser exibido no painel.
  • executeCommand: O Func<ExecuteCommandContext, Task<ExecuteCommandResult>> ser executado quando o comando é invocado, que é onde a lógica de comando é implementada.
  • updateState: O retorno de chamada Func<UpdateCommandStateContext, ResourceCommandState> é invocado para determinar o estado de "ativado" do comando, que é usado para habilitar ou desabilitar o comando no painel.
  • iconName: O nome do ícone a ser exibido no painel. O ícone é opcional, mas quando você o fornece, ele deve ser um Fluent UI válido Blazor nome do ícone.
  • iconVariant: A variante do ícone a ser exibido no painel, as opções válidas são Regular (padrão) ou Filled.

Executar lógica de comando

O delegado executeCommand é onde a lógica de comando é implementada. Este parâmetro é definido como um Func<ExecuteCommandContext, Task<ExecuteCommandResult>>. O ExecuteCommandContext fornece as seguintes propriedades:

  • ExecuteCommandContext.ServiceProvider: A instância IServiceProvider usada para resolver serviços.
  • ExecuteCommandContext.ResourceName: O nome da instância de recurso na qual o comando está sendo executado.
  • ExecuteCommandContext.CancellationToken: O CancellationToken usado para cancelar a execução do comando.

No exemplo anterior, o delegado executeCommand é implementado como um método async que limpa a cache do recurso Redis. Ele delega a uma função privada de escopo de classe chamada OnRunClearCacheCommandAsync para executar a limpeza de cache real. Considere o seguinte código:

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

O código anterior:

  • Recupera a cadeia de conexão do recurso Redis.
  • Conecta-se à instância Redis.
  • Obtém a instância do banco de dados.
  • Executa o comando FLUSHALL para limpar o cache.
  • Retorna uma instância CommandResults.Success() para indicar que o comando foi bem-sucedido.

Atualizar lógica de estado do comando

A delegação updateState é onde o estado do comando é determinado. Este parâmetro é definido como um Func<UpdateCommandStateContext, ResourceCommandState>. O UpdateCommandStateContext fornece as seguintes propriedades:

  • UpdateCommandStateContext.ServiceProvider: A instância IServiceProvider usada para resolver serviços.
  • UpdateCommandStateContext.ResourceSnapshot: O instantâneo da instância de recurso na qual o comando está sendo executado.

O instantâneo imutável é uma instância de CustomResourceSnapshot, que revela todos os tipos de detalhes valiosos sobre a instância de recurso. Considere o seguinte código:

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

O código anterior:

  • Recupera a instância do registrador do provedor de serviços.
  • Registra os detalhes do instantâneo do recurso.
  • Retorna ResourceCommandState.Enabled se o recurso estiver em boas condições; caso contrário, retorna ResourceCommandState.Disabled.

Testar o comando personalizado

Para testar o comando personalizado, atualize o arquivo de Program.cs do projeto de host do aplicativo para incluir o seguinte código:

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

O código anterior chama o método de extensão WithClearCommand para adicionar o comando personalizado ao recurso Redis. Execute o aplicativo e navegue até o painel .NET.NET Aspire. Você deve ver o comando personalizado listado sob o recurso Redis. Na página de Recursos do painel, selecione o botão de reticências na coluna de Ações:

.NET Aspire painel de controlo: Redis recurso de cache com comando personalizado exibido.

A imagem anterior mostra o comando Limpar cache que foi adicionado ao recurso Redis. O ícone é exibido quando um coelho risca para indicar que a velocidade do recurso dependente está sendo limpa.

Selecione o comando Limpar cache para limpar o cache do recurso Redis. O comando deve ser executado com êxito e o cache deve ser limpo:

.NET Aspire dashboard: Redis recurso de cache com execução de comando personalizado.

Ver também