Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tento článek ukazuje, jak použít osvědčené vzory architektury pro desktopové aplikace WinUI 3 vytvořené pomocí Windows App SDK. Dozvíte se, jak nastavit vkládání závislostí, spravovat konfiguraci a strukturovat kód pro podnikové provozní scénáře (LOB).
Předpoklady
- Windows App SDK 1.5 nebo novější
- .NET 8 nebo novější
- Visual Studio 2022 verze 17.10 nebo novější s úlohami vývoje .NET desktopů a Windows aplikací
Injektáž závislostí
Desktopové aplikace WinUI 3 neobsahují integrovaný kontejner injektáže závislostí (DI), jako je ASP.NET Core, ale můžete ho přidat pomocí stejného Microsoft.Extensions.DependencyInjection balíčku NuGet. Di umožňuje testovatelný, volně propojený a snadněji udržovatelný kód.
Nastavení kontejneru DI
Nainstalujte balíček NuGet:
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Hosting
Nakonfigurujte hostitele a služby ve vašem App.xaml.cs:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.UI.Xaml;
public partial class App : Application
{
public IHost Host { get; }
public static T GetService<T>() where T : class
{
if ((App.Current as App)!.Host.Services.GetService(typeof(T)) is not T service)
{
throw new ArgumentException(
$"{typeof(T)} needs to be registered in ConfigureServices.");
}
return service;
}
public App()
{
InitializeComponent();
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
.UseContentRoot(AppContext.BaseDirectory)
.ConfigureServices((context, services) =>
{
// Services
services.AddSingleton<INavigationService, NavigationService>();
services.AddSingleton<IDataService, DataService>();
services.AddTransient<IDialogService, DialogService>();
// ViewModels
services.AddTransient<MainViewModel>();
services.AddTransient<SettingsViewModel>();
// Views
services.AddTransient<MainPage>();
services.AddTransient<SettingsPage>();
})
.Build();
}
}
Note
Pokud zapomenete zaregistrovat službu, výše uvedený pomocník GetService<T>() za běhu vyvolá výjimku ArgumentException s názvem chybějícího typu. Spusťte aplikaci a během vývoje přejděte na každou stránku, abyste ověřili správnost všech registrací.
Vložení závislostí do modelů ViewModels
Když je kontejner nakonfigurovaný, objekty ViewModels přijímají závislosti prostřednictvím injektáže konstruktoru:
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public partial class MainViewModel : ObservableObject
{
private readonly IDataService _dataService;
private readonly INavigationService _navigationService;
public MainViewModel(IDataService dataService, INavigationService navigationService)
{
_dataService = dataService;
_navigationService = navigationService;
}
[ObservableProperty]
private string _statusMessage = string.Empty;
[RelayCommand]
private async Task LoadDataAsync()
{
StatusMessage = "Loading...";
var items = await _dataService.GetItemsAsync();
StatusMessage = $"Loaded {items.Count} items";
}
}
Životnost služeb
Při registraci služeb zvolte odpovídající dobu životnosti:
| Životnost | Metoda | Použít pro |
|---|---|---|
| Singleton | AddSingleton<T>() |
Navigace, stav celé aplikace, mezipaměti |
| Rozsahem | AddScoped<T>() |
Kontexty pro jednotlivá okna nebo dialogy |
| Přechodný | AddTransient<T>() |
ViewModels, bezstavové služby |
Tip
Zaregistrujte modely ViewModel jako přechodné , aby každá navigace vytvořila novou instanci. Zaregistrujte služby, které uchovávají stav celé aplikace jako Singleton.
Správa konfigurace
Slouží Microsoft.Extensions.Configuration ke správě nastavení aplikací v desktopových aplikacích, stejně jako v ASP.NET Core.
Přidání podpory konfigurace
Nainstalujte požadované balíčky:
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Options
Vytvořte appsettings.json soubor v kořenovém adresáři projektu. V Průzkumník řešení klikněte na soubor pravým tlačítkem myši, vyberte Vlastnosti a nastavte Kopírovat do výstupního adresáře na Kopírovat, pokud je novější. Případně přidejte <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> k záznamu souboru v .csproj.
{
"AppSettings": {
"ApiBaseUrl": "https://api.contoso.com/v2",
"MaxRetryCount": 3,
"EnableTelemetry": true
},
"Logging": {
"LogLevel": "Information"
}
}
Vytvořte vazbu konfigurace v nastavení DI:
.ConfigureServices((context, services) =>
{
// Bind settings to a strongly-typed class
services.Configure<AppSettings>(
context.Configuration.GetSection("AppSettings"));
// Inject IOptions<AppSettings> into services
services.AddSingleton<IApiClient, ApiClient>();
})
Použití konfigurace ve službách
using Microsoft.Extensions.Options;
public class ApiClient : IApiClient
{
private readonly AppSettings _settings;
private readonly HttpClient _httpClient;
public ApiClient(IOptions<AppSettings> options)
{
_settings = options.Value;
_httpClient = new HttpClient
{
BaseAddress = new Uri(_settings.ApiBaseUrl)
};
}
}
Trvalost uživatelských nastavení
Pro uživatelská nastavení, která přežijí aktualizace aplikací, použijte Windows.Storage.ApplicationData (zabalené aplikace) nebo místní soubor JSON (rozbalené aplikace):
public class UserSettingsService : IUserSettingsService
{
private readonly string _settingsPath;
public UserSettingsService()
{
var localAppData = Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData);
_settingsPath = Path.Combine(localAppData, "Contoso", "MyApp", "settings.json");
}
public async Task SaveAsync<T>(string key, T value)
{
var settings = await LoadAllAsync();
settings[key] = JsonSerializer.Serialize(value);
Directory.CreateDirectory(Path.GetDirectoryName(_settingsPath)!);
await File.WriteAllTextAsync(
_settingsPath, JsonSerializer.Serialize(settings));
}
}
Note
Zabalené aplikace (MSIX) mohou používat ApplicationData.Current.LocalSettings pro jednoduché dvojice klíč–hodnota. Rozbalené aplikace musí spravovat vlastní umístění úložiště.
Hlavní příznaky
Implementujte příznaky funkcí, které umožňují postupné zavádění a testování A/B bez opětovného nasazení.
Lokální přepínače funkcí s konfigurací
public interface IFeatureFlagService
{
bool IsEnabled(string featureName);
}
public class FeatureFlagService : IFeatureFlagService
{
private readonly Dictionary<string, bool> _flags;
public FeatureFlagService(IConfiguration configuration)
{
_flags = configuration.GetSection("FeatureFlags")
.Get<Dictionary<string, bool>>() ?? new();
}
public bool IsEnabled(string featureName) =>
_flags.TryGetValue(featureName, out var enabled) && enabled;
}
Integrace služby Azure App Configuration
U příznaků funkcí spravovaných v cloudu použijte Azure App Configuration:
dotnet add package Microsoft.Extensions.Configuration.AzureAppConfiguration
dotnet add package Microsoft.FeatureManagement
using Azure.Identity;
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, config) =>
{
config.AddAzureAppConfiguration(options =>
{
options.Connect(
new Uri("https://<your-store>.azconfig.io"),
new DefaultAzureCredential())
.UseFeatureFlags(flagOptions =>
{
flagOptions.CacheExpirationInterval = TimeSpan.FromMinutes(5);
});
});
})
.ConfigureServices((context, services) =>
{
services.AddFeatureManagement(context.Configuration);
})
.Build();
Note
Pro místní vývoj můžete místo toho použít připojovací řetězec DefaultAzureCredential. Uložte připojovací řetězec do proměnné prostředí nebo do Správce přihlašovacích údajů systému Windows — nikdy ne do systému správy verzí:
options.Connect(Environment.GetEnvironmentVariable("APP_CONFIG_CONNECTION_STRING"))
Tip
Informace o postupném zavádění založeném na obchodě na úrovni balíčků najdete v části Postupné zavádění balíčků.
Podnikové a LOB vzory
Obchodní aplikace mají další požadavky na identitu, ochranu dat a správu zařízení.
Identita a podmíněný přístup
Pro podnikové ověřování použijte knihovnu MSAL (Identity a ověřování Microsoftu):
services.AddSingleton<IAuthService>(sp =>
{
var app = PublicClientApplicationBuilder
.Create("your-client-id")
.WithAuthority(AzureCloudInstance.AzurePublic, "your-tenant-id")
.WithRedirectUri("http://localhost")
.Build();
return new AuthService(app);
});
Important
http://localhost identifikátor URI pro přesměrování je vhodný pro vývoj. V případě produkčních desktopových aplikací použijte místo toho zprostředkovatele Windows (WAM), který poskytuje jednotné přihlašování s účtem Windows uživatele a silnější ochranou tokenů.
Podnikové aplikace nasazené prostřednictvím Intune můžou vynutit zásady podmíněného přístupu, které vyžadují:
- Dodržování předpisů zařízením (šifrování, PIN kód, verze operačního systému)
- Vícefaktorové ověřování
- Omezení síťového umístění
Offline data a ukládání do cache
Desktopové aplikace LOB často potřebují fungovat offline. Implementujte vzor repozitáře s lokální mezipamětí:
public class CachedRepository<T> : IRepository<T> where T : class, IEntity
{
private readonly IApiClient _apiClient;
private readonly ILocalDatabase _localDb;
public async Task<IReadOnlyList<T>> GetAllAsync(bool forceRefresh = false)
{
if (!forceRefresh)
{
var cached = await _localDb.GetAllAsync<T>();
if (cached.Any())
return cached;
}
try
{
var items = await _apiClient.GetAsync<List<T>>();
await _localDb.UpsertAllAsync(items);
return items;
}
catch (HttpRequestException)
{
// Offline fallback
return await _localDb.GetAllAsync<T>();
}
}
}
Ochrana dat
Používejte Windows.Security.Cryptography.DataProtection (zabalené aplikace) nebo .NET DataProtectionProvider pro šifrování citlivých místních dat.
Nainstalujte požadovaný balíček:
dotnet add package Microsoft.AspNetCore.DataProtection.Extensions
Pak zaregistrujte ochranu dat v kontejneru DI:
using Microsoft.AspNetCore.DataProtection;
services.AddDataProtection()
.SetApplicationName("Contoso.LOBApp")
.ProtectKeysWithDpapi();
Vícevrstvé architektury
Strukturujte aplikaci WinUI 3 ve vrstvách a udržujte tak závislosti v jednom směru:
┌─────────────────────────────┐
│ Views (XAML + code-behind)│ ← UI layer, no business logic
├─────────────────────────────┤
│ ViewModels (MVVM Toolkit) │ ← Presentation logic, commands
├─────────────────────────────┤
│ Services / Use Cases │ ← Business rules, orchestration
├─────────────────────────────┤
│ Repositories / Data │ ← Data access, API clients, caching
└─────────────────────────────┘
Pravidla:
- Každá vrstva závisí pouze na vrstvě přímo pod ní.
- Modely ViewModel nikdy neodkazují na typy uživatelského rozhraní (
Page,Window,ContentDialog). - Služby definují rozhraní; implementace žijí v datové vrstvě.
- Zaregistrujte všechny závislosti mezi vrstvami v kontejneru DI.
Zpětná kompatibilita a správa verzí
Při vydání nových verzí aplikace zvažte:
- Migrace dat: Verzujte schéma místní databáze. Pomocí spouštěče migrace při spuštění proveďte upgrade z předchozího schématu na aktuální schéma.
- Migrace nastavení: Uložte verzi schématu do souboru nastavení. Při načítání použijte transformace ze starých formátů na nové.
- Souběžné instalace: MSIX ve výchozím nastavení upgraduje balíček. Pokud chcete spustit více hlavních verzí vedle sebe, přiřaďte každé verzi jedinečný název rodiny balíčků v době návrhu.
public class DatabaseMigrator
{
public async Task MigrateAsync(SqliteConnection db)
{
var currentVersion = await GetSchemaVersionAsync(db);
if (currentVersion < 2)
await ApplyMigration_v2(db);
if (currentVersion < 3)
await ApplyMigration_v3(db);
await SetSchemaVersionAsync(db, LatestVersion);
}
}
Ověření nastavení
Spusťte aplikaci a projděte všechny stránky, abyste ověřili, že se služby správně načítají. Pokud služba není zaregistrovaná, za běhu se zobrazí InvalidOperationException s názvem chybějícího typu. Také potvrďte, že:
- Hodnoty konfigurace se načítají z
appsettings.json(zkontrolujte vázanou vlastnost v ladicím programu). - Příznaky funkcí se vyhodnocují podle očekávání (přepněte příznak a restartujte aplikaci).
- Ukládání do mezipaměti v režimu offline poskytuje data, když není k dispozici síť.
Související obsah
Windows developer