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

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

.NET MAUI 10 + Aspire: Інтеграція 2026

Вступ: навіщо мобільним додаткам .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» вам на допомогу.

Про Автора Devika Ramaswamy

Devika spent four years on the Xamarin team at Microsoft before the transition to .NET MAUI, where she worked on the iOS handler layer and shipped fixes that landed in the .NET 7 and .NET 8 release notes. She left Redmond in 2023 to run mobile engineering at a Series B logistics startup, porting their 600k-line Xamarin.Forms codebase to MAUI over eleven months. She writes mostly about the unglamorous parts of cross-platform work: handler internals, AOT trimming on iOS, MSBuild target customization, and why your hot reload keeps breaking. She holds the .NET MAUI MVP award (2024, 2025) and has spoken at .NET Conf and Xamarin Expert Day. Based in Bengaluru, she still pushes the occasional PR to the dotnet/maui repo on weekends.