.NET MAUI та .NET Aspire: покроковий посібник з інтеграції

Покроковий посібник з інтеграції .NET MAUI 10 із .NET Aspire. Налаштування Service Discovery, Dev Tunnels для Android та iOS емуляторів, телеметрія через OpenTelemetry та моніторинг на дашборді Aspire — з прикладами коду.

Вступ: навіщо мобільним додаткам .NET Aspire

Якщо ви коли-небудь розробляли .NET MAUI додаток, що має працювати з локальним API, то напевно знаєте цей біль. На Windows все чудово — localhost і поїхали. Але варто запустити емулятор Android — і раптом потрібна адреса 10.0.2.2. На iOS симуляторі — своя історія. А на фізичному пристрої — ще й сертифікати налаштовуй.

Чесно кажучи, це кожного разу з'їдає мінімум годину. І щоразу доводиться гуглити заново.

.NET Aspire вирішує цю проблему раз і назавжди.

З випуском .NET MAUI 10 Microsoft офіційно представила інтеграцію з .NET Aspire — хмарно-орієнтованим стеком для побудови спостережуваних розподілених додатків. Тепер ваш мобільний додаток автоматично знаходить локальні сервіси без жорстко закодованих URL-адрес, а Dev Tunnels забезпечують прозоре підключення з емуляторів і фізичних пристроїв.

Отже, давайте розберемо покроково, як це все налаштувати — від створення проєкту до моніторингу телеметрії на дашборді Aspire.

Що таке .NET Aspire і чому він важливий для мобільної розробки

.NET Aspire — це набір інструментів, шаблонів та NuGet-пакетів для побудови розподілених хмарних додатків. По суті, він дає вам чотири ключові речі:

  • Оркестрація — управління декількома сервісами та їхніми залежностями через єдиний App Host проєкт
  • Service Discovery — автоматичне виявлення сервісів без необхідності вказувати конкретні URL-адреси
  • Телеметрія — вбудована підтримка OpenTelemetry для збору метрик, логів і розподілених трейсів
  • Стійкість — патерни повторних спроб, circuit breaker та інші механізми для надійної роботи з мережею

До появи інтеграції з MAUI ці можливості були доступні лише для веб-додатків та мікросервісів. Тепер мобільні розробники нарешті отримують ті самі інструменти (і це, повірте, відчувається).

Ключові переваги для мобільної розробки

Інтеграція Aspire з .NET MAUI вирішує три найбільші болі мобільних розробників:

  1. Автоматична конфігурація мережі — більше не потрібно вручну обробляти 10.0.2.2 для Android або налаштовувати валідацію сертифікатів
  2. Service Discovery — ваш MAUI додаток знаходить та підключається до сервісів за іменем, а не за URL
  3. Dev Tunnels — вбудована підтримка тунелів для підключення емуляторів iOS та Android до сервісів на вашій машині

Звучить добре в теорії? На практиці — ще краще. Давайте налаштуємо.

Передумови та підготовка середовища

Перш ніж розпочати, переконайтеся що у вас є:

  • .NET 10 SDK або новіша версія
  • Visual Studio 2022 (v17.12+) або Visual Studio 2026 із робочим навантаженням .NET MAUI
  • .NET Aspire workload — встановлюється через CLI
  • Docker Desktop — для контейнерних залежностей (бази даних, Redis тощо)

Встановіть необхідні компоненти:

# Встановити .NET Aspire workload
dotnet workload install aspire

# Перевірити встановлення
dotnet workload list

# Переконатися, що шаблони доступні
dotnet new list | grep maui-aspire

Архітектура рішення: три ключові проєкти

Типове рішення .NET MAUI + Aspire складається з трьох проєктів. Нічого зайвого — кожен має свою чітку роль.

1. App Host — оркестратор

App Host — це серце вашої Aspire-інфраструктури. Він реєструє всі сервіси, налаштовує залежності та запускає дашборд моніторингу. Саме тут визначається, як сервіси знаходять один одного.

2. MAUI Service Defaults — конфігурація за замовчуванням

Містить спільну конфігурацію для Service Discovery, телеметрії та патернів стійкості. Створюється зі спеціального шаблону maui-aspire-servicedefaults, оптимізованого саме для мобільних додатків.

3. MAUI App — ваш мобільний додаток

Власне ваш .NET MAUI додаток, який підключається до Service Defaults і використовує Service Discovery для зв'язку з API.

Якщо схематично: App Host оркеструє всі сервіси та створює Dev Tunnels → MAUI Service Defaults надає конфігурацію → MAUI App використовує цю конфігурацію для підключення до сервісів за іменами. Просто і зрозуміло.

Покрокове налаштування інтеграції

Крок 1: Створення MAUI Service Defaults

Service Defaults — це спеціальний проєкт із розширеннями для підключення телеметрії та Service Discovery. Створюємо через CLI:

# Створити проєкт Service Defaults
dotnet new maui-aspire-servicedefaults -n MyApp.MauiServiceDefaults

# Додати посилання з MAUI проєкту на Service Defaults
dotnet add MyApp.csproj reference MyApp.MauiServiceDefaults/MyApp.MauiServiceDefaults.csproj

Після створення в проєкті з'явиться файл Extensions.cs з набором розширювальних методів. Ось як він виглядає:

// Extensions.cs у проєкті MAUI Service Defaults
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

namespace MyApp.MauiServiceDefaults;

public static class Extensions
{
    public static MauiAppBuilder AddServiceDefaults(this MauiAppBuilder builder)
    {
        builder.ConfigureOpenTelemetry();
        builder.AddServiceDiscovery();
        builder.Services.ConfigureHttpClientDefaults(http =>
        {
            // Увімкнути Service Discovery для всіх HttpClient
            http.AddServiceDiscovery();

            // Увімкнути стандартні обробники стійкості
            http.AddStandardResilienceHandler();
        });

        return builder;
    }

    private static MauiAppBuilder ConfigureOpenTelemetry(
        this MauiAppBuilder builder)
    {
        builder.Services.AddOpenTelemetry()
            .WithMetrics(metrics =>
            {
                metrics.AddRuntimeInstrumentation()
                       .AddHttpClientInstrumentation();
                // Розкоментуйте для метрик MAUI SDK:
                // .AddMeter("Microsoft.Maui");
            })
            .WithTracing(tracing =>
            {
                tracing.AddHttpClientInstrumentation();
                // Розкоментуйте для трейсів MAUI SDK:
                // .AddSource("Microsoft.Maui");
            });

        builder.Services.AddOpenTelemetryExporters(builder.Configuration);

        return builder;
    }
}

Важливо: Service Defaults — виключно для спільної конфігурації. Не додавайте сюди моделі, сервіси або бізнес-логіку — для цього використовуйте окремий Class Library.

Крок 2: Налаштування App Host

App Host — це точка входу для оркестрації. Тут реєструєте API сервіси та підключаєте MAUI додаток. Ось тут, власне, починається найцікавіше:

// Program.cs в AppHost проєкті
var builder = DistributedApplication.CreateBuilder(args);

// Додати API сервіс
var weatherApi = builder.AddProject<Projects.WeatherApi>("webapi");

// Додати MAUI додаток (через метод AddMauiProject, НЕ через .csproj)
var mauiApp = builder.AddMauiProject("mauiapp");

// Для Windows та Mac Catalyst — пряме підключення через localhost
mauiApp.AddWindows()
    .WithReference(weatherApi);

mauiApp.AddMacCatalyst()
    .WithReference(weatherApi);

// Для iOS та Android — потрібен Dev Tunnel
var publicDevTunnel = builder.AddDevTunnel("devtunnel-public")
    .WithAnonymousAccess()
    .WithReference(weatherApi.GetEndpoint("https"));

mauiApp.AddiOSSimulator()
    .WithOtlpDevTunnel()
    .WithReference(weatherApi, publicDevTunnel);

mauiApp.AddAndroidEmulator()
    .WithOtlpDevTunnel()
    .WithReference(weatherApi, publicDevTunnel);

builder.Build().Run();

Тут є кілька нюансів, на які варто звернути увагу:

  • AddMauiProject використовується замість стандартного AddProject, оскільки MAUI додатки мають особливий процес збірки
  • Windows і Mac Catalyst працюють напряму через localhost — Dev Tunnel їм не потрібен
  • Для iOS та Android створюється публічний Dev Tunnel з анонімним доступом
  • WithOtlpDevTunnel() створює окремий тунель для передачі телеметрії

Крок 3: Конфігурація MauiProgram.cs

Тепер у вашому MAUI додатку підключіть Service Defaults і налаштуйте HTTP клієнт:

// MauiProgram.cs
using MyApp.MauiServiceDefaults;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        // Підключити Service Defaults (Service Discovery + телеметрія)
        builder.AddServiceDefaults();

        // Налаштувати типізований HTTP клієнт із Service Discovery
        builder.Services.AddHttpClient<WeatherApiClient>(client =>
        {
            // Ім'я сервісу збігається з іменем у App Host
            // https+http:// — спочатку HTTPS, якщо недоступний — HTTP
            client.BaseAddress = new Uri("https+http://webapi");
        });

        return builder.Build();
    }
}

Зверніть увагу на URI https+http://webapi. Це не звичайна URL-адреса — це спеціальний синтаксис Service Discovery. webapi — ім'я ресурсу з App Host. Aspire автоматично перетворить його на реальну адресу, враховуючи платформу.

Тобто на Android він піде через тунель, на Windows — через localhost. І вам взагалі не потрібно про це думати. Ось це, мабуть, і є головна принадність всієї інтеграції.

Крок 4: Створення типізованого API клієнта

Типізовані клієнти — рекомендована практика в .NET. І в контексті Aspire вони працюють особливо гарно, бо Service Discovery підхоплює все автоматично:

// WeatherApiClient.cs
using System.Net.Http.Json;

public class WeatherApiClient
{
    private readonly HttpClient _httpClient;

    public WeatherApiClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<WeatherForecast[]?> GetForecastAsync(
        CancellationToken cancellationToken = default)
    {
        return await _httpClient.GetFromJsonAsync<WeatherForecast[]>(
            "/weatherforecast",
            cancellationToken);
    }
}

public record WeatherForecast(
    DateOnly Date,
    int TemperatureC,
    string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

Крок 5: Використання клієнта у ViewModel

Інтегруємо типізований клієнт через DI — тут все стандартно для MVVM, нічого нового:

// MainPageViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;

public partial class MainPageViewModel : ObservableObject
{
    private readonly WeatherApiClient _weatherClient;

    public MainPageViewModel(WeatherApiClient weatherClient)
    {
        _weatherClient = weatherClient;
    }

    [ObservableProperty]
    private bool _isLoading;

    public ObservableCollection<WeatherForecast> Forecasts { get; } = new();

    [RelayCommand]
    private async Task LoadWeatherAsync()
    {
        try
        {
            IsLoading = true;
            var data = await _weatherClient.GetForecastAsync();

            Forecasts.Clear();
            if (data is not null)
            {
                foreach (var forecast in data)
                    Forecasts.Add(forecast);
            }
        }
        catch (HttpRequestException ex)
        {
            // Обробка помилок мережі
            await Shell.Current.DisplayAlertAsync(
                "Помилка", $"Не вдалося завантажити дані: {ex.Message}", "OK");
        }
        finally
        {
            IsLoading = false;
        }
    }
}

Як працює Service Discovery на різних платформах

Мабуть, найкрутіша частина інтеграції — це повністю прозора робота Service Discovery на всіх платформах. Давайте подивимось, що відбувається "під капотом".

Windows та Mac Catalyst

Тут все максимально просто. Додаток і сервіси працюють на одній машині, тому Service Discovery просто перетворює ім'я сервісу на адресу localhost з відповідним портом. Ніяких тунелів, ніякої додаткової конфігурації.

Android емулятор

А от тут раніше починався головний біль. Емулятор Android працює у віртуальній машині з власним мережевим стеком. Історично для підключення до хост-машини використовувалася адреса 10.0.2.2 — і це потрібно було пам'ятати (або щоразу гуглити, як більшість з нас і робила).

З Aspire інтеграцією ви про це більше не думаєте — Dev Tunnel автоматично створює безпечний тунель від емулятора до ваших локальних сервісів.

iOS симулятор

iOS симулятор теж підключається через Dev Tunnel. Це, між іншим, вирішує ще й проблему з SSL-сертифікатами, яка раніше вимагала окремого танцю з бубном.

Фізичні пристрої

Для фізичних пристроїв Dev Tunnels стають ще ціннішими. Вони створюють публічний HTTPS ендпоінт, доступний з будь-якої мережі. Це означає, що для тестування на реальному пристрої більше не обов'язково бути в одній Wi-Fi мережі з машиною розробника.

Серйозна перевага, особливо якщо ви працюєте з QA-командою, яка сидить в іншому офісі (чи взагалі на ремоуті).

Генерація AspireAppSettings.g.cs

Цікавий технічний момент: на відміну від серверних додатків, де Aspire передає конфігурацію через змінні середовища, для MAUI все працює інакше. Налаштування генеруються як вихідний код у файлі AspireAppSettings.g.cs і включаються в додаток під час компіляції.

При першому запуску App Host цей файл автоматично створюється у директорії вашого MAUI проєкту. Він містить адреси сервісів, параметри Dev Tunnels, ендпоінти OTLP — все, що потрібно для роботи.

// AspireAppSettings.g.cs (генерується автоматично)
// Не редагуйте цей файл вручну!
namespace MyApp;

internal static class AspireAppSettings
{
    // Конфігурація генерується App Host при запуску
    // та включає адреси сервісів, тунелів і т.д.
}

Важливо: обов'язково додайте цей файл до .gitignore. Він містить адреси Dev Tunnels, специфічні для конкретної машини — коміт цього файлу в репозиторій лише створить плутанину для колег.

Моніторинг через Aspire Dashboard

Чесно кажучи, дашборд Aspire — це одна з тих речей, які побачивши раз, ви не зрозумієте, як жили без неї. Повна картина роботи вашого розподіленого додатка в реальному часі — і все це з коробки, без жодного рядка додаткової конфігурації.

Запуск та доступ до дашборда

Дашборд стартує автоматично при запуску App Host:

# Запустити оркестратор
dotnet run --project MyApp.AppHost

# Або через Aspire CLI
aspire run

Після запуску відкриється Aspire Dashboard у браузері. Там ви побачите всі зареєстровані сервіси і зможете моніторити їхній стан.

Що доступно на дашборді

  • Структуровані логи — всі логи від усіх сервісів та MAUI додатка в одному місці з фільтрацією та пошуком
  • Розподілені трейси — повний ланцюжок виклику від натискання кнопки в MAUI до відповіді API, з хронометражем кожного етапу
  • Метрики — HTTP запити, час відповіді, помилки, використання ресурсів
  • Стан здоров'я — health check ендпоінти для кожного сервісу

Увімкнення метрик MAUI SDK

За замовчуванням дашборд збирає лише стандартні метрики HTTP-клієнта та runtime. Якщо хочете побачити детальну інформацію про макети, рендеринг та інші внутрішні операції .NET MAUI — розкоментуйте відповідні рядки у Extensions.cs:

.WithMetrics(metrics =>
{
    metrics.AddRuntimeInstrumentation()
           .AddHttpClientInstrumentation()
           // Увімкнути метрики MAUI SDK
           .AddMeter("Microsoft.Maui");
})
.WithTracing(tracing =>
{
    tracing.AddHttpClientInstrumentation()
           // Увімкнути трейси MAUI SDK
           .AddSource("Microsoft.Maui");
})

Тільки майте на увазі — це може генерувати значний обсяг телеметрії. Для розробки та профілювання — чудово, але в production будьте обережні з цим.

Обробка помилок та патерни стійкості

Мобільні додатки живуть у реальному світі нестабільного інтернету. Метро, підвал, природа — Wi-Fi зникає в найневідповідніший момент. Тому правильна обробка помилок — це не "nice to have", а необхідність. Aspire автоматично налаштовує стандартні обробники стійкості через Microsoft.Extensions.Http.Resilience.

Стандартний обробник стійкості

Коли ви викликаєте AddStandardResilienceHandler() у Service Defaults, автоматично додаються:

  • Повторні спроби (Retry) — автоматичне повторення невдалих HTTP запитів з експоненційною затримкою
  • Circuit Breaker — захист від лавини запитів до сервісу, що не відповідає
  • Таймаути — обмеження часу очікування відповіді
  • Rate Limiting — обмеження частоти запитів

Все це працює прозоро. Вам не потрібно писати додатковий код у ваших API-клієнтах — стійкість вже вшита.

Кастомізація стратегій

Якщо стандартна конфігурація не підходить (а для мобільних додатків часто варто налаштувати більші таймаути), ви можете кастомізувати параметри:

builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
    client.BaseAddress = new Uri("https+http://webapi");
})
.AddStandardResilienceHandler(options =>
{
    // Збільшити кількість повторних спроб для нестабільних мереж
    options.Retry.MaxRetryAttempts = 5;

    // Збільшити таймаут для повільних мобільних мереж
    options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(10);
    options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(60);
});

Практичні поради та типові помилки

Чекліст запуску

Якщо ваш MAUI додаток не може підключитися до сервісів — не панікуйте. Перевірте ці пункти (з досвіду — зазвичай проблема в одному з перших трьох):

  1. Переконайтеся, що AddServiceDefaults() викликається в MauiProgram.cs
  2. Ім'я сервісу у BaseAddress має точно збігатися з іменем у App Host
  3. Перевірте схему https+http:// — пропущений +http одна з найпоширеніших помилок
  4. Перевірте посилання на Service Defaults проєкт у .csproj
  5. Переконайтеся, що App Host запущено перед запуском MAUI додатка
  6. Для iOS/Android — перевірте, що Dev Tunnel створено та доступний

Порядок запуску

Важливий момент, який легко пропустити: MAUI додаток не запускається автоматично при старті App Host. Правильний порядок такий:

  1. Запустіть App Host (dotnet run --project AppHost)
  2. Дочекайтеся появи Aspire Dashboard
  3. Запустіть MAUI додаток як окремий проєкт

Так, це трохи незвично, якщо ви звикли що F5 запускає все. Але це зроблено свідомо — MAUI проєкт має свій специфічний процес деплойменту на пристрій чи емулятор.

Файл .gitignore

Обов'язково додайте згенеровані файли до .gitignore:

# Aspire generated settings
**/AspireAppSettings.g.cs

Продуктивна збірка

Service Discovery та Dev Tunnels — це виключно інструменти розробки. Для production вам потрібно конфігурувати реальні URL-адреси API через стандартні механізми .NET MAUI (appsettings.json, змінні середовища або серверну конфігурацію). Не забудьте про це перед релізом — я бачив, як кілька команд пропускали цей крок і дивувались, чому додаток не працює після публікації.

Інтеграція з існуючим проєктом

Якщо у вас вже є .NET MAUI 10 проєкт, додати Aspire нескладно — буквально кілька команд:

# 1. Створити Service Defaults
dotnet new maui-aspire-servicedefaults -n YourApp.MauiServiceDefaults

# 2. Додати посилання
dotnet add YourApp.csproj reference YourApp.MauiServiceDefaults/YourApp.MauiServiceDefaults.csproj

# 3. Створити App Host проєкт
dotnet new aspire-apphost -n YourApp.AppHost

# 4. Додати NuGet пакет для MAUI підтримки
dotnet add YourApp.AppHost.csproj package Aspire.Hosting.Maui --prerelease

Далі модифікуйте MauiProgram.cs, додавши виклик builder.AddServiceDefaults(), та замініть жорстко закодовані URL-адреси на синтаксис Service Discovery. Весь процес — хвилин 15-20, якщо не відволікатись.

Поточний статус та обмеження

Станом на березень 2026 року, інтеграція .NET MAUI з Aspire знаходиться у стадії Preview. Що це означає на практиці:

  • API може змінюватися між версіями — будьте готові до breaking changes
  • Повна інтеграція з Visual Studio 2026 ще в процесі
  • Деякі сценарії (наприклад, кілька MAUI проєктів в одному App Host) можуть працювати нестабільно
  • Пакет Aspire.Hosting.Maui має позначку --prerelease

Але, чесно кажучи, для повсякденної розробки і локального тестування інтеграція вже цілком робоча. Навіть у Preview стані вона суттєво спрощує життя мобільним розробникам — і, на мою думку, вже варта того, щоб спробувати у наступному проєкті.

FAQ — часті запитання

Чи можна використовувати .NET Aspire у production для мобільних додатків?

Aspire інтеграція з MAUI переважно призначена для середовища розробки — Service Discovery, Dev Tunnels та дашборд допомагають під час локальної роботи. Для production слід конфігурувати реальні URL-адреси API. Однак компоненти телеметрії (OpenTelemetry) цілком підходять і для production — якщо ви експортуєте метрики до Azure Application Insights або іншого OTLP-сумісного бекенду.

Чи обов'язково мати Docker для роботи з Aspire?

Ні, Docker потрібен лише якщо ваші сервіси використовують контейнерні залежності — PostgreSQL, Redis, RabbitMQ тощо. Якщо ваш App Host оркеструє тільки .NET проєкти, Docker не обов'язковий.

Чи працює Aspire інтеграція з .NET MAUI Blazor Hybrid?

Так, і це чудова новина. Blazor Hybrid додатки підтримуються повністю. Процес налаштування ідентичний звичайному MAUI додатку — BlazorWebView компоненти можуть використовувати ті самі типізовані HTTP клієнти через DI, нічого додатково конфігурувати не потрібно.

Як працює Dev Tunnel і чи є обмеження безпеки?

Dev Tunnel створює безпечний HTTPS тунель через інфраструктуру Microsoft. Тунелі з анонімним доступом (WithAnonymousAccess()) доступні без автентифікації — для розробки це зручно, але не використовуйте їх з реальними чутливими даними. Для більш безпечних сценаріїв можна налаштувати автентифікацію через Microsoft Entra ID.

Яка мінімальна версія .NET MAUI підтримує Aspire?

Інтеграція доступна починаючи з .NET MAUI 10 (.NET 10). Для більш ранніх версій підключення до локальних сервісів доведеться налаштовувати вручну — документація Microsoft «Connect to local web services» вам на допомогу.

Про Автора Editorial Team

Our team of expert writers and editors.