.NET .NET Aspire 中的自定义资源命令

.NET .NET Aspire 应用模型 中的每个资源都表示为 IResource,当添加到 分布式应用程序生成器时,它是 IResourceBuilder<T> 接口的泛型类型参数。 可以使用 资源生成器 API 链接调用、配置基础资源,在某些情况下,可能需要向资源添加自定义命令。 创建自定义命令的一些常见情况可能是运行数据库迁移或播种/重置数据库。 本文介绍如何将自定义命令添加到清除缓存的 Redis 资源。

重要

这些 .NET.NET Aspire 仪表板 命令仅在本地运行仪表板时才可用。 在 Azure Container Apps中运行仪表板时,它们不可用。

将自定义命令添加到资源

首先从 可用模板创建新的 .NET.NET Aspire Starter App。 若要从此模板创建解决方案,请遵循 快速入门:生成第一个 .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> 实例以允许链式调用。

WithCommand API 将适当的注释添加到资源,这些注释会在 .NET.NET Aspire 仪表板中被使用。 仪表板使用这些注释在 UI 中呈现命令。 在深入了解这些详细信息之前,我们先确保先了解 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 委托被实现为一种用于清除 Redis 资源缓存的 async 方法。 它将委托给名为 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 显示自定义命令的缓存资源。

上述图像显示了已添加在 Redis 资源中的 清除缓存 命令。 图标显示为兔子交叉,以指示正在清除依赖资源的速度。

选择 清除缓存 命令以清除 Redis 资源的缓存。 该命令应成功执行,应清除缓存:

.NET Aspire 仪表板:使用自定义命令执行 Redis 缓存资源。

另请参阅