Поделиться через


Пользовательские команды ресурсов в .NET.NET Aspire

Каждый ресурс в модели приложения .NET.NET Aspire представлен в виде IResource, а при добавлении в построитель распределенных приложений , является параметром универсального типа интерфейса IResourceBuilder<T>. Вы используете построитель ресурсов API для цепочки вызовов, настройки базового ресурса, и в некоторых ситуациях вы можете захотеть добавить в ресурс пользовательские команды. Некоторые распространенные сценарии для создания пользовательской команды могут включать выполнение миграций базы данных или начального заполнения/сброса базы данных. В этой статье вы узнаете, как добавить пользовательскую команду в ресурс Redis, который очищает кэш.

Важный

Эти команды .NET.NET Aspire панели мониторинга доступны только при локальном запуске панели мониторинга. Они недоступны при запуске панели мониторинга в Azure Container Apps.

Добавление пользовательских команд в ресурс

Начните с создания нового начального приложения .NET.NET Aspire из доступных шаблонов . Чтобы создать решение из этого шаблона, выполните краткое руководство: Создать свое первое .NET.NET Aspire решение. После создания этого решения добавьте новый класс с именем RedisResourceBuilderExtensions.cs в проект узла приложения . Замените содержимое файла следующим кодом:

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

Предыдущий код:

  • Делит пространство имен Aspire.Hosting, чтобы оно было видимым для проекта хоста приложения.
  • Это static class, чтобы он смог содержать методы расширения.
  • Он определяет один метод расширения с именем WithClearCommand, расширяющий интерфейс IResourceBuilder<RedisResource>.
  • Метод WithClearCommand регистрирует команду с именем clear-cache, которая очищает кэш ресурса Redis.
  • Метод WithClearCommand возвращает экземпляр IResourceBuilder<RedisResource> для возможности цепного вызова.

API WithCommand добавляет соответствующие аннотации к ресурсу, которые используются на панели мониторинга .NET.NET Aspire. Панель мониторинга использует эти аннотации для отрисовки команды в пользовательском интерфейсе. Прежде чем углубляться в эти сведения, давайте сначала убедимся, что вы понимаете параметры метода WithCommand:

  • name: имя вызываемой команды.
  • displayName: имя команды, отображаемой на панели мониторинга.
  • executeCommand: Func<ExecuteCommandContext, Task<ExecuteCommandResult>> для выполнения при вызове команды, в которой реализуется логика команды.
  • updateState: вызывается обратный вызов Func<UpdateCommandStateContext, ResourceCommandState>, чтобы определить состояние "включено" команды, которое используется для включения или отключения команды на панели мониторинга.
  • iconName: имя значка, отображаемого на панели мониторинга. Значок необязателен, но если вы предоставите его, он должен быть допустимым именем значка Fluent UI Blazor.
  • iconVariant: вариант значка, отображаемого на панели мониторинга, допустимые параметры Regular (по умолчанию) или Filled.

Выполнение логики команды

Делегат executeCommand это место, где реализована логика команды. Этот параметр определяется как Func<ExecuteCommandContext, Task<ExecuteCommandResult>>. ExecuteCommandContext предоставляет следующие свойства:

  • ExecuteCommandContext.ServiceProvider: экземпляр IServiceProvider, используемый для разрешения сервисов.
  • ExecuteCommandContext.ResourceName: имя экземпляра ресурса, в который выполняется команда.
  • ExecuteCommandContext.CancellationToken: CancellationToken, которая используется для отмены выполнения команды.

В предыдущем примере делегат executeCommand реализуется как метод async, который очищает кэш ресурса Redis. Он передаёт выполнение фактической очистки кэша на функцию с областью видимости частного класса, именуемую OnRunClearCacheCommandAsync. Рассмотрим следующий код:

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

Предыдущий код:

  • Извлекает строку подключения из ресурса Redis.
  • Подключается к экземпляру Redis.
  • Возвращает экземпляр базы данных.
  • Выполняет команду FLUSHALL для очистки кэша.
  • Возвращает экземпляр CommandResults.Success(), чтобы указать, что команда выполнена успешно.

Обновление логики состояния команды

Делегат updateState определяет состояние команды. Этот параметр определяется как Func<UpdateCommandStateContext, ResourceCommandState>. UpdateCommandStateContext предоставляет следующие свойства:

  • UpdateCommandStateContext.ServiceProvider: экземпляр IServiceProvider, используемый для разрешения сервисов.
  • UpdateCommandStateContext.ResourceSnapshot: моментальный снимок экземпляра ресурса, на который выполняется команда.

Неизменяемый моментальный снимок — это экземпляр CustomResourceSnapshot, который предоставляет все ценные сведения об экземпляре ресурса. Рассмотрим следующий код:

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

Предыдущий код:

  • Извлекает экземпляр логгера из поставщика услуг.
  • Записывает сведения о моментальном снимке ресурсов.
  • Возвращает ResourceCommandState.Enabled, если ресурс в норме; в противном случае возвращает ResourceCommandState.Disabled.

Проверка настраиваемой команды

Чтобы проверить пользовательскую команду, обновите файл Program.cs узлового проекта приложения, чтобы включить следующий код:

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

Предыдущий код вызывает метод расширения WithClearCommand, чтобы добавить пользовательскую команду в ресурс Redis. Запустите приложение и перейдите на панель мониторинга .NET.NET Aspire. Вы увидите настраиваемую команду, указанную в ресурсе Redis. На странице Ресурсы панели управления нажмите кнопку с многоточием в столбце Действия:

панель мониторинга .NET Aspire: Redis ресурс кэша с отображаемой пользовательской командой.

На предыдущем рисунке показана команда Clear cache, добавленная в ресурс Redis. Значок отображается в виде вычеркнутого кролика, чтобы указать, что скорость зависимого ресурса сбрасывается.

Выберите команду Очистить кэш, чтобы очистить кэш ресурса Redis. Команда должна выполниться успешно, и кэш должен быть очищен:

панель мониторинга .NET Aspire: Redis ресурс кэша с выполненной пользовательской командой.

См. также