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


Создание пользовательских интеграций клиентов .NET.NET Aspire

Эта статья является продолжением статьи Создание пользовательских .NET.NET Aspire интеграций хостинга. Он поможет вам создать интеграцию клиента .NET.NET Aspire, которая использует MailKit для отправки сообщений электронной почты. Затем эта интеграция добавляется в приложение бюллетеня, которое вы ранее создали. В предыдущем примере было опущено создание клиентской интеграции, и вместо этого положились на существующий .NETSmtpClient. Лучше всего использовать SmtpClient MailKit по сравнению с официальным .NETSmtpClient для отправки сообщений электронной почты, так как это более современно и поддерживает больше функций и протоколов. Дополнительные сведения см. в статье .NET SmtpClient: примечания.

Необходимые условия

Если вы следуете инструкциям, то у вас должно быть приложение для рассылки, полученное в результате выполнения шагов, описанных в статье Create custom .NET.NET Aspire hosting integration.

Совет

Эта статья вдохновлена существующими интеграциями .NET.NET Aspire и основана на официальном руководстве команды. Есть места, где указанные рекомендации различаются, и важно понять причину различий. Дополнительные сведения см. в .NET.NET Aspire требованиях к интеграции.

Создание библиотеки для интеграции

.NET .NET Aspire интеграции предоставляются в виде пакетов NuGet, но в этом примере публикация пакета NuGet выходит за рамки обсуждаемой в статье темы. Вместо этого создается проект библиотеки классов, содержащий интеграцию и ссылающийся на него как проект. .NET .NET Aspire пакеты интеграции предназначены для упаковки клиентской библиотеки, такой как MailKit, и предоставления готовых к работе телеметрии, проверок работоспособности, настраиваемости и возможности тестирования. Начнем с создания проекта библиотеки классов.

  1. Создайте проект библиотеки классов с именем MailKit.Client в том же каталоге, что и MailDevResource.sln из предыдущей статьи.

    dotnet new classlib -o MailKit.Client
    
  2. Добавьте проект в решение.

    dotnet sln ./MailDevResource.sln add MailKit.Client/MailKit.Client.csproj
    

Следующий шаг — добавить все пакеты NuGet, от которых зависит интеграция. Вместо того чтобы вы добавляли каждый пакет по одному в .NET CLI, вероятно, проще скопировать и вставить следующий XML-код в MailKit.Client.csproj-файл.

<ItemGroup>
  <PackageReference Include="MailKit" Version="4.11.0" />
  <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.3" />
  <PackageReference Include="Microsoft.Extensions.Resilience" Version="9.3.0" />
  <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.3" />
  <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.3" />
  <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.11.2" />
</ItemGroup>

Определение параметров интеграции

Каждый раз, когда вы создаете интеграцию .NET.NET Aspire, лучше всего понять, с какой клиентской библиотекой вы сопоставляете. С помощью MailKit необходимо понять параметры конфигурации, необходимые для подключения к серверу SMTP. Но также важно понимать, поддерживает ли библиотека проверки работоспособности, трассировку и метрики. MailKit поддерживает прослеживание и метрики с помощью своего Telemetry.SmtpClient класса. При добавлении проверки работоспособностиследует по возможности использовать любые установленные или существующие проверки работоспособности. В противном случае можно рассмотреть возможность разработки собственного решения для интеграции. Добавьте следующий код в проект MailKit.Client в файл с именем MailKitClientSettings.cs:

using System.Data.Common;

namespace MailKit.Client;

/// <summary>
/// Provides the client configuration settings for connecting MailKit to an SMTP server.
/// </summary>
public sealed class MailKitClientSettings
{
    internal const string DefaultConfigSectionName = "MailKit:Client";

    /// <summary>
    /// Gets or sets the SMTP server <see cref="Uri"/>.
    /// </summary>
    /// <value>
    /// The default value is <see langword="null"/>.
    /// </value>
    public Uri? Endpoint { get; set; }

    /// <summary>
    /// Gets or sets a boolean value that indicates whether the database health check is disabled or not.
    /// </summary>
    /// <value>
    /// The default value is <see langword="false"/>.
    /// </value>
    public bool DisableHealthChecks { get; set; }

    /// <summary>
    /// Gets or sets a boolean value that indicates whether the OpenTelemetry tracing is disabled or not.
    /// </summary>
    /// <value>
    /// The default value is <see langword="false"/>.
    /// </value>
    public bool DisableTracing { get; set; }

    /// <summary>
    /// Gets or sets a boolean value that indicates whether the OpenTelemetry metrics are disabled or not.
    /// </summary>
    /// <value>
    /// The default value is <see langword="false"/>.
    /// </value>
    public bool DisableMetrics { get; set; }

    internal void ParseConnectionString(string? connectionString)
    {
        if (string.IsNullOrWhiteSpace(connectionString))
        {
            throw new InvalidOperationException($"""
                    ConnectionString is missing.
                    It should be provided in 'ConnectionStrings:<connectionName>'
                    or '{DefaultConfigSectionName}:Endpoint' key.'
                    configuration section.
                    """);
        }

        if (Uri.TryCreate(connectionString, UriKind.Absolute, out var uri))
        {
            Endpoint = uri;
        }
        else
        {
            var builder = new DbConnectionStringBuilder
            {
                ConnectionString = connectionString
            };
            
            if (builder.TryGetValue("Endpoint", out var endpoint) is false)
            {
                throw new InvalidOperationException($"""
                        The 'ConnectionStrings:<connectionName>' (or 'Endpoint' key in
                        '{DefaultConfigSectionName}') is missing.
                        """);
            }

            if (Uri.TryCreate(endpoint.ToString(), UriKind.Absolute, out uri) is false)
            {
                throw new InvalidOperationException($"""
                        The 'ConnectionStrings:<connectionName>' (or 'Endpoint' key in
                        '{DefaultConfigSectionName}') isn't a valid URI.
                        """);
            }

            Endpoint = uri;
        }
    }
}

Предыдущий код определяет класс MailKitClientSettings с:

  • Endpoint свойство, представляющее строку подключения к SMTP-серверу.
  • DisableHealthChecks свойство, определяющее, включены ли проверки состояния.
  • DisableTracing свойство, определяющее, включена ли трассировка.
  • DisableMetrics свойство, определяющее, включены ли метрики.

Логика разбора строки подключения

Класс параметров также содержит метод ParseConnectionString, который анализирует строку подключения в валидный Uri. Ожидается, что конфигурация будет предоставлена в следующем формате:

  • ConnectionStrings:<connectionName>: строка подключения к SMTP-серверу.
  • MailKit:Client:ConnectionString: строка подключения к SMTP-серверу.

Если ни ни из этих значений не указано, создается исключение.

Предоставление функциональных возможностей клиента

Целью интеграции .NET.NET Aspire является предоставление базовой клиентской библиотеки потребителям путем внедрения зависимостей. С помощью MailKit и для этого примера класс SmtpClient — это тот, который вы хотите сделать доступным. Вы не оборачиваете какую-либо функциональность, а сопоставляете настройки конфигурации с классом SmtpClient. Обычно для интеграции можно предоставлять как стандартные, так и ключевые регистрации служб. Стандартные регистрации используются при наличии только одного экземпляра службы, а регистрация служб с ключами используется при наличии нескольких экземпляров службы. Иногда для получения нескольких регистраций одного типа используют шаблон фабрики. Добавьте следующий код в проект MailKit.Client в файл с именем MailKitClientFactory.cs:

using MailKit.Net.Smtp;

namespace MailKit.Client;

/// <summary>
/// A factory for creating <see cref="ISmtpClient"/> instances
/// given a <paramref name="smtpUri"/> (and optional <paramref name="credentials"/>).
/// </summary>
/// <param name="settings">
/// The <see cref="MailKitClientSettings"/> settings for the SMTP server
/// </param>
public sealed class MailKitClientFactory(MailKitClientSettings settings) : IDisposable
{
    private readonly SemaphoreSlim _semaphore = new(1, 1);

    private SmtpClient? _client;

    /// <summary>
    /// Gets an <see cref="ISmtpClient"/> instance in the connected state
    /// (and that's been authenticated if configured).
    /// </summary>
    /// <param name="cancellationToken">Used to abort client creation and connection.</param>
    /// <returns>A connected (and authenticated) <see cref="ISmtpClient"/> instance.</returns>
    /// <remarks>
    /// Since both the connection and authentication are considered expensive operations,
    /// the <see cref="ISmtpClient"/> returned is intended to be used for the duration of a request
    /// (registered as 'Scoped') and is automatically disposed of.
    /// </remarks>
    public async Task<ISmtpClient> GetSmtpClientAsync(
        CancellationToken cancellationToken = default)
    {
        await _semaphore.WaitAsync(cancellationToken);

        try
        {
            if (_client is null)
            {
                _client = new SmtpClient();

                await _client.ConnectAsync(settings.Endpoint, cancellationToken)
                             .ConfigureAwait(false);
            }
        }
        finally
        {
            _semaphore.Release();
        }       

        return _client;
    }

    public void Dispose()
    {
        _client?.Dispose();
        _semaphore.Dispose();
    }
}

Класс MailKitClientFactory — это фабрика, которая создает экземпляр ISmtpClient на основе параметров конфигурации. Он отвечает за возврат реализации ISmtpClient, которая имеет активное подключение к настроенному SMTP-серверу. Затем необходимо предоставить возможность потребителям зарегистрировать эту фабрику в контейнере внедрения зависимостей. Добавьте следующий код в проект MailKit.Client в файл с именем MailKitExtensions.cs:

using MailKit;
using MailKit.Client;
using MailKit.Net.Smtp;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.Extensions.Hosting;

/// <summary>
/// Provides extension methods for registering a <see cref="SmtpClient"/> as a
/// scoped-lifetime service in the services provided by the <see cref="IHostApplicationBuilder"/>.
/// </summary>
public static class MailKitExtensions
{
    /// <summary>
    /// Registers 'Scoped' <see cref="MailKitClientFactory" /> for creating
    /// connected <see cref="SmtpClient"/> instance for sending emails.
    /// </summary>
    /// <param name="builder">
    /// The <see cref="IHostApplicationBuilder" /> to read config from and add services to.
    /// </param>
    /// <param name="connectionName">
    /// A name used to retrieve the connection string from the ConnectionStrings configuration section.
    /// </param>
    /// <param name="configureSettings">
    /// An optional delegate that can be used for customizing options.
    /// It's invoked after the settings are read from the configuration.
    /// </param>
    public static void AddMailKitClient(
        this IHostApplicationBuilder builder,
        string connectionName,
        Action<MailKitClientSettings>? configureSettings = null) =>
        AddMailKitClient(
            builder,
            MailKitClientSettings.DefaultConfigSectionName,
            configureSettings,
            connectionName,
            serviceKey: null);

    /// <summary>
    /// Registers 'Scoped' <see cref="MailKitClientFactory" /> for creating
    /// connected <see cref="SmtpClient"/> instance for sending emails.
    /// </summary>
    /// <param name="builder">
    /// The <see cref="IHostApplicationBuilder" /> to read config from and add services to.
    /// </param>
    /// <param name="name">
    /// The name of the component, which is used as the <see cref="ServiceDescriptor.ServiceKey"/> of the
    /// service and also to retrieve the connection string from the ConnectionStrings configuration section.
    /// </param>
    /// <param name="configureSettings">
    /// An optional method that can be used for customizing options. It's invoked after the settings are
    /// read from the configuration.
    /// </param>
    public static void AddKeyedMailKitClient(
        this IHostApplicationBuilder builder,
        string name,
        Action<MailKitClientSettings>? configureSettings = null)
    {
        ArgumentNullException.ThrowIfNull(name);

        AddMailKitClient(
            builder,
            $"{MailKitClientSettings.DefaultConfigSectionName}:{name}",
            configureSettings,
            connectionName: name,
            serviceKey: name);
    }

    private static void AddMailKitClient(
        this IHostApplicationBuilder builder,
        string configurationSectionName,
        Action<MailKitClientSettings>? configureSettings,
        string connectionName,
        object? serviceKey)
    {
        ArgumentNullException.ThrowIfNull(builder);

        var settings = new MailKitClientSettings();

        builder.Configuration
               .GetSection(configurationSectionName)
               .Bind(settings);

        if (builder.Configuration.GetConnectionString(connectionName) is string connectionString)
        {
            settings.ParseConnectionString(connectionString);
        }

        configureSettings?.Invoke(settings);

        if (serviceKey is null)
        {
            builder.Services.AddScoped(CreateMailKitClientFactory);
        }
        else
        {
            builder.Services.AddKeyedScoped(serviceKey, (sp, key) => CreateMailKitClientFactory(sp));
        }

        MailKitClientFactory CreateMailKitClientFactory(IServiceProvider _)
        {
            return new MailKitClientFactory(settings);
        }

        if (settings.DisableHealthChecks is false)
        {
            builder.Services.AddHealthChecks()
                .AddCheck<MailKitHealthCheck>(
                    name: serviceKey is null ? "MailKit" : $"MailKit_{connectionName}",
                    failureStatus: default,
                    tags: []);
        }

        if (settings.DisableTracing is false)
        {
            builder.Services.AddOpenTelemetry()
                .WithTracing(
                    traceBuilder => traceBuilder.AddSource(
                        Telemetry.SmtpClient.ActivitySourceName));
        }

        if (settings.DisableMetrics is false)
        {
            // Required by MailKit to enable metrics
            Telemetry.SmtpClient.Configure();

            builder.Services.AddOpenTelemetry()
                .WithMetrics(
                    metricsBuilder => metricsBuilder.AddMeter(
                        Telemetry.SmtpClient.MeterName));
        }
    }
}

Предыдущий код добавляет два метода расширения для типа IHostApplicationBuilder, один для стандартной регистрации MailKit и другой для регистрации с ключами MailKit.

Совет

Методы расширения для интеграции .NET.NET Aspire должны расширить тип IHostApplicationBuilder и следовать соглашению об именовании Add<MeaningfulName>, где <MeaningfulName> является типом или функциональностью, которую вы добавляете. В этой статье метод расширения AddMailKitClient используется для добавления клиента MailKit. Скорее всего, использование AddMailKitSmtpClient вместо AddMailKitClientбудет больше соответствовать официальным рекомендациям, поскольку это регистрирует только SmtpClient, а не всю библиотеку MailKit.

В итоге оба расширения полагаются на частный метод AddMailKitClient, чтобы зарегистрировать MailKitClientFactory в контейнере внедрения зависимостей как область службы . Причина регистрации MailKitClientFactory в качестве ограниченной службы заключается в том, что операции подключения считаются дорогостоящими и следует повторно использовать в той же области, где это возможно. Другими словами, для одного запроса следует использовать тот же ISmtpClient экземпляр. Фабрика сохраняет экземпляр SmtpClient, который она создает, и затем удаляет его.

Привязка конфигурации

Одной из первых вещей, которые выполняет частная реализация методов AddMailKitClient, является привязка параметров конфигурации к классу MailKitClientSettings. Класс параметров создаётся, и затем функция Bind вызывается с определённым разделом конфигурации. Затем вызывается необязательный configureSettings делегат с текущими параметрами. Это позволяет потребителю дополнительно настроить параметры, гарантируя, что параметры кода вручную имеют приоритет над параметрами конфигурации. После этого в зависимости от того, было ли предоставлено значение serviceKey, MailKitClientFactory следует зарегистрировать в контейнере внедрения зависимостей в качестве стандартной или ключевой службы.

Важный

Специально задумано, чтобы при регистрации сервисов вызывалась перегрузка implementationFactory. Метод CreateMailKitClientFactory вызывается, если конфигурация недопустима. Это гарантирует, что создание MailKitClientFactory откладывается до тех пор, пока в нем не возникнет необходимость, и предотвращает ошибку приложения до того, как журналирование станет доступным.

Регистрация проверок работоспособности, а также телеметрии, более подробно описана в следующих разделах.

Добавить проверки работоспособности

проверки состояния здоровья — это способ мониторинга состояния здоровья интеграций. С помощью MailKit можно проверить работоспособность подключения к SMTP-серверу. Добавьте следующий код в проект MailKit.Client в файл с именем MailKitHealthCheck.cs:

using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace MailKit.Client;

internal sealed class MailKitHealthCheck(MailKitClientFactory factory) : IHealthCheck
{
    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default)
    {
        try
        {
            // The factory connects (and authenticates).
            _ = await factory.GetSmtpClientAsync(cancellationToken);

            return HealthCheckResult.Healthy();
        }
        catch (Exception ex)
        {
            return HealthCheckResult.Unhealthy(exception: ex);
        }
    }
}

Предыдущая реализация проверки состояния здоровья:

  • Реализует интерфейс IHealthCheck.
  • Принимает MailKitClientFactory в качестве основного параметра конструктора.
  • Удовлетворяет методу CheckHealthAsync следующими способами:
    • Попытка получить экземпляр ISmtpClient из factory. При успешном выполнении возвращается HealthCheckResult.Healthy.
    • Если выбрасывается исключение, возвращается HealthCheckResult.Unhealthy.

Как ранее сообщалось в ходе регистрации MailKitClientFactory, MailKitHealthCheck условно зарегистрирован вместе с IHeathChecksBuilder.

if (settings.DisableHealthChecks is false)
{
    builder.Services.AddHealthChecks()
        .AddCheck<MailKitHealthCheck>(
            name: serviceKey is null ? "MailKit" : $"MailKit_{connectionName}",
            failureStatus: default,
            tags: []);
}

Потребитель может исключить проверки работоспособности, задав для свойства DisableHealthChecks значение true в конфигурации. Распространенный шаблон интеграции предполагает наличие необязательных функций, и .NET.NET Aspire интеграции настоятельно поощряют такие виды конфигураций. Дополнительные сведения о проверках работоспособности и рабочем примере, включающем пользовательский интерфейс, см. в примере .NET AspireASP.NET Core HealthChecksUI.

Подключить телеметрию

Как лучшая практика, клиентская библиотека MailKit предоставляет доступ к данным телеметрии. .NET .NET Aspire может воспользоваться этой телеметрией и отобразить ее на панели мониторинга .NET.NET Aspire. В зависимости от того, включены ли трассировка и метрики, телеметрия подключается, как показано в следующем фрагменте кода.

if (settings.DisableTracing is false)
{
    builder.Services.AddOpenTelemetry()
        .WithTracing(
            traceBuilder => traceBuilder.AddSource(
                Telemetry.SmtpClient.ActivitySourceName));
}

if (settings.DisableMetrics is false)
{
    // Required by MailKit to enable metrics
    Telemetry.SmtpClient.Configure();

    builder.Services.AddOpenTelemetry()
        .WithMetrics(
            metricsBuilder => metricsBuilder.AddMeter(
                Telemetry.SmtpClient.MeterName));
}

Обновить службу новостной рассылки

С помощью созданной библиотеки интеграции теперь можно обновить службу бюллетеня, чтобы использовать клиент MailKit. Первым шагом является добавление ссылки на проект MailKit.Client. Добавьте ссылку на проект MailKitClient.csproj в проект MailDevResource.NewsletterService.

dotnet add ./MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj reference MailKit.Client/MailKit.Client.csproj

Затем добавьте ссылку на проект ServiceDefaults:

dotnet add ./MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj reference MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj

Последний шаг — заменить существующий файл Program.cs в проекте MailDevResource.NewsletterService следующим кодом C#:

using System.Net.Mail;
using MailKit.Client;
using MailKit.Net.Smtp;
using MimeKit;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// Add services to the container.
builder.AddMailKitClient("maildev");

var app = builder.Build();

app.MapDefaultEndpoints();

// Configure the HTTP request pipeline.

app.UseSwagger();
app.UseSwaggerUI();
app.UseHttpsRedirection();

app.MapPost("/subscribe",
    async (MailKitClientFactory factory, string email) =>
{
    ISmtpClient client = await factory.GetSmtpClientAsync();

    using var message = new MailMessage("newsletter@yourcompany.com", email)
    {
        Subject = "Welcome to our newsletter!",
        Body = "Thank you for subscribing to our newsletter!"
    };

    await client.SendAsync(MimeMessage.CreateFromMailMessage(message));
});

app.MapPost("/unsubscribe",
    async (MailKitClientFactory factory, string email) =>
{
    ISmtpClient client = await factory.GetSmtpClientAsync();

    using var message = new MailMessage("newsletter@yourcompany.com", email)
    {
        Subject = "You are unsubscribed from our newsletter!",
        Body = "Sorry to see you go. We hope you will come back soon!"
    };

    await client.SendAsync(MimeMessage.CreateFromMailMessage(message));
});

app.Run();

Наиболее заметными изменениями в предыдущем коде являются:

  • Обновленные инструкции using, которые включают пространства имен MailKit.Client, MailKit.Net.Smtp и MimeKit.
  • Замена регистрации для официального .NETSmtpClient вызовом метода расширения AddMailKitClient.
  • Замена вызовов для карт /subscribe и /unsubscribe на внедрение MailKitClientFactory и использование экземпляра ISmtpClient для отправки email.

Выполнить пример

Теперь, когда вы создали клиентскую интеграцию MailKit и обновили службу бюллетеня, чтобы использовать ее, можно запустить пример. В интегрированной среде разработки выберите F5 или запустите dotnet run из корневого каталога решения, чтобы запустить приложение — вы должны увидеть панель мониторинга .NET.NET Aspire.

.NET Aspire Панель мониторинга:MailDev ресурсы информационного бюллетеня работают.

После запуска приложения перейдите к интерфейсу Swagger по адресу https://localhost:7251/swagger и протестируйте конечные точки /subscribe и /unsubscribe. Щелкните стрелку вниз, чтобы развернуть конечную точку:

Интерфейс Swagger: конечная точка подписки.

Затем нажмите кнопку Try it out. Введите адрес электронной почты и нажмите кнопку Execute.

Swagger UI: эндпоинт подписки, используя адрес электронной почты.

Повторите это несколько раз, чтобы добавить несколько адресов электронной почты. Вы увидите сообщение электронной почты, отправленное в папку "Входящие" MailDev:

MailDev папка Входящие с несколькими электронными письмами.

Остановите приложение, выбрав CTRL+C в окне терминала, где запущено приложение, или нажав кнопку остановки в интегрированной среде разработки.

Просмотр телеметрии MailKit

Клиентская библиотека MailKit предоставляет данные телеметрии, которые можно просмотреть на панели мониторинга .NET.NET Aspire. Чтобы просмотреть данные телеметрии, перейдите на панель мониторинга .NET.NET Aspirehttps://localhost:7251. Выберите ресурс newsletter, чтобы просмотреть данные телеметрии на странице метрик .

панель мониторинга: телеметрия MailKit. .NET.NET Aspire

Снова откройте пользовательский интерфейс Swagger и выполните некоторые запросы к конечным точкам /subscribe и /unsubscribe. Затем вернитесь к панели мониторинга .NET.NET Aspire и выберите ресурс newsletter. Выберите метрику в узле mailkit.net.smtp, например mailkit.net.smtp.client.operation.count. Вы должны увидеть данные телеметрии для клиента MailKit:

панель мониторинга .NET.NET Aspire: телеметрия MailKit для подсчета операций.

Сводка

В этой статье вы узнали, как создать интеграцию .NET.NET Aspire, которая использует MailKit для отправки сообщений электронной почты. Вы также узнали, как интегрировать эту интеграцию с приложением бюллетеня, которое вы ранее создали. Вы узнали о основных принципах интеграций .NETи.NET Aspire, например, обеспечение доступа к базовой клиентской библиотеке потребителям путем инъекции зависимостей, а также добавление мониторинга состояния и сбора данных телеметрии в интеграцию. Вы также узнали, как обновить службу бюллетеня для использования клиента MailKit.

Идите вперед и создайте собственные интеграции .NET.NET Aspire. Если вы считаете, что в построенной интеграции достаточно ценности сообщества, рассмотрите возможность публикации его в качестве пакета NuGet, для других пользователей. Кроме того, подумайте о том, чтобы отправить пулл-реквест в репозиторий .NET AspireGitHub, чтобы его могли рассмотреть для включения в официальные интеграции .NET.NET Aspire.

Дальнейшие действия