资源注释 Aspire

批注是一种关键扩展性机制 Aspire ,可用于将元数据和行为附加到资源。 它们提供了一种方法,可以自定义资源在整个应用生命周期中的配置、部署和管理方式。 本文介绍了批注的工作原理以及如何在应用程序中 Aspire 有效地使用这些注释。

什么是批注

Aspire中的批注是实现IResourceAnnotation接口的对象。 它们附加到资源,以提供其他元数据、配置或行为。 注释由堆栈的各个 Aspire 部分使用,包括:

  • 用于显示自定义 URL 和命令的仪表板。
  • 用于生成基础结构即代码的部署工具。
  • 用于配置运行时行为的托管层。
  • 测试基础结构进行资源检查。

每个批注都与特定资源相关联,可以包含扩展该资源功能所需的任何数据或行为。

批注的工作原理

将资源添加到应用模型时,可以使用各种扩展方法附加批注。 这些批注随资源一起存储,可由系统的不同组件检索和处理。

下面是如何使用批注的简单示例:

var builder = DistributedApplication.CreateBuilder(args);

var redis = builder.AddRedis("cache")
    .WithCommand("clear-cache", "Clear Cache", 
        async context => new ExecuteCommandResult { Success = true })
    .WithUrl("admin", "http://localhost:8080/admin");

builder.Build().Run();

在本示例中:

内置批注类型

Aspire 包括许多常见方案的内置批注类型。 本部分介绍 一些 较常用的批注,但有 更多 可用于特定用例。

EndpointAnnotation

资源的网络终结点由 EndpointAnnotation 定义。 它包含有关端口、协议和终结点配置的信息。

var api = builder.AddProject<Projects.Api>("api")
    .WithEndpoint(callback: endpoint =>
    {
        endpoint.Port = 5000;
        endpoint.IsExternal = true;
        endpoint.Protocol = Protocol.Tcp;
        endpoint.Transport = "http";
    });

ResourceUrlAnnotation

ResourceUrlAnnotation 定义了显示在仪表板上的自定义URL,这些URL通常指向管理接口或文档。

var database = builder.AddPostgres("postgres")
    .WithUrl("admin", "https://localhost:5050");

EnvironmentCallbackAnnotation

EnvironmentCallbackAnnotation 使您能够在运行时根据其他资源的状态修改环境变量。

// This is typically used internally by WithReference
var app = builder.AddProject<Projects.MyApp>("app")
    .WithReference(database);

ContainerMountAnnotation

定义 ContainerMountAnnotation 容器化资源的卷装载。

var postgres = builder.AddPostgres("postgres")
    .WithDataVolume(); // Adds a ContainerMountAnnotation

PublishingCallbackAnnotation

PublishingCallbackAnnotation 允许您注册在发布过程中执行的回调。 这对于在发布资源时执行自定义作非常有用。

var api = builder.AddProject<Projects.Api>("api")
    .WithPublishingCallback(async context =>
    {
        // Custom logic during publishing
        await CustomPublishLogicAsync(context);
    });

有关详细信息,请参阅 WithPublishingCallback

DeployingCallbackAnnotation

DeployingCallbackAnnotation 允许您注册在部署过程中执行的回调。 此批注由部署工具在内部用于自定义资源部署行为。

var api = builder.AddProject<Projects.Api>("api");

api.Resource.Annotations.Add(
    new DeployingCallbackAnnotation(async context =>
    {
        // Custom deployment logic
        await ConfigureDeploymentAsync(context);
    });
);

有关发布和部署 Aspire 应用的详细信息,请参阅 发布和部署

创建自定义批注

自定义 Aspire 注释旨在捕获可在应用程序生命周期中利用的资源特定的元数据和行为。 它们通常由以下对象使用:

  • 用于推断用户意向和配置资源行为的扩展方法
  • 用于生成特定于部署的配置的部署工具
  • 生命周期挂钩 用于查询应用模型并做出运行时决策。
  • 用于显示资源信息的开发工具,如仪表板。

自定义批注应侧重于单个关注点,并为资源元数据的使用者提供明确的值。

1.定义批注类

首先创建实现的 IResourceAnnotation类。 专注于收集其他组件需要用以分析资源的特定元数据。

public sealed class ServiceMetricsAnnotation : IResourceAnnotation
{
    public string MetricsPath { get; set; } = "/metrics";
    public int Port { get; set; } = 9090;
    public bool Enabled { get; set; } = true;
    public string[] AdditionalLabels { get; set; } = [];
}

2. 创建扩展方法

创建遵循 Aspire生成器模式的 fluent 扩展方法。 这些方法应使 AppHost 作者能够轻松配置批注:

public static class ServiceMetricsExtensions
{
    public static IResourceBuilder<T> WithMetrics<T>(
        this IResourceBuilder<T> builder,
        string path = "/metrics",
        int port = 9090,
        params string[] labels)
        where T : class, IResource
    {
        var annotation = new ServiceMetricsAnnotation
        {
            MetricsPath = path,
            Port = port,
            Enabled = true,
            AdditionalLabels = labels
        };

        return builder.WithAnnotation(annotation);
    }

    public static IResourceBuilder<T> WithoutMetrics<T>(
        this IResourceBuilder<T> builder)
        where T : class, IResource
    {
        return builder.WithAnnotation(new ServiceMetricsAnnotation { Enabled = false });
    }
}

扩展方法充当您的注解的主要 API 表面。 它们应:

  • 遵循命名约定(对于加法运算,请以 With 开头)。
  • 提供合理的默认值。
  • 返回用于方法链的构建器。
  • 包括全面的 XML 文档。

3.使用自定义批注

定义后,批注会无缝集成到 AppHost 模型中:

var builder = DistributedApplication.CreateBuilder(args);

var api = builder.AddProject<Projects.Api>("api")
    .WithMetrics("/api/metrics", 9090, "environment:production", "service:api");

使用批注进行测试

编写测试时,可以检查批注以验证资源配置:

[Fact]
public async Task Resource_Should_Have_Expected_Annotations()
{
    var appHost = await DistributedApplicationTestingBuilder
        .CreateAsync<Projects.MyApp_AppHost>();

    await using var app = await appHost.BuildAsync();

    var resource = app.Resources.GetResource("my-resource");

    // Assert that specific annotations exist
    Assert.NotEmpty(resource.Annotations.OfType<ServiceMetricsAnnotation>());

    // Assert annotation properties
    var metricsConfig = resource.Annotations
        .OfType<ServiceMetricsAnnotation>()
        .First();
    
    Assert.True(metricsConfig.Enabled);
    Assert.Equal("/metrics", metricsConfig.MetricsPath);
}

有关详细信息,请参阅 测试 Aspire

最佳做法

使用批注时,请考虑以下最佳做法:

使用有意义的名称

为批注类和属性选择描述性名称:

// Good
public sealed class DatabaseConnectionPoolAnnotation : IResourceAnnotation

// Avoid
public sealed class DbAnnotation : IResourceAnnotation

遵循生成器模式

创建遵循 Aspire“建造者模式”的流畅扩展方法:

var resource = builder.AddMyResource("name")
    .WithCustomBehavior()
    .WithAnotherFeature();

记录您的批注

为自定义注释和扩展方法提供 XML 文档:

/// <summary>
/// Configures custom caching behavior for the resource.
/// </summary>
/// <param name="builder">The resource builder.</param>
/// <param name="ttl">The time-to-live for cached items.</param>
/// <returns>The resource builder for chaining.</returns>
public static IResourceBuilder<T> WithCaching<T>(
    this IResourceBuilder<T> builder,
    TimeSpan ttl)
    where T : class, IResource { /*...*/ }

使批注保持简单

批注应侧重于单个责任:

// Good - single responsibility
public sealed class RetryPolicyAnnotation : IResourceAnnotation
{
    public int MaxRetries { get; set; }
    public TimeSpan Delay { get; set; }
}

// Avoid - multiple responsibilities
public sealed class ConfigAnnotation : IResourceAnnotation
{
    public RetryPolicy RetryPolicy { get; set; }
    public LoggingSettings Logging { get; set; }
    public SecuritySettings Security { get; set; }
}

后续步骤