Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit artikel wordt beschreven hoe u bewezen architectuurpatronen toepast op WinUI 3-desktop-apps die zijn gebouwd met de Windows App SDK. U leert hoe u afhankelijkheidsinjectie kunt instellen, configuratie kunt beheren en code kunt structureren voor enterprise-Line-of-Business (LOB)-scenario's.
Prerequisites
- Windows App SDK 1,5 of hoger
- .NET 8 of hoger
- Visual Studio 2022 versie 17.10 of later met de workloads .NET-desktopontwikkeling en Windows-applicatieontwikkeling
Afhankelijkheidsinjectie
WinUI 3-desktop-apps bevatten geen ingebouwde afhankelijkheidsinjectiecontainer (DI), zoals ASP.NET Core wel, maar u kunt er een toevoegen met hetzelfde Microsoft.Extensions.DependencyInjection NuGet-pakket. DI maakt uw code testbaar, losjes gekoppeld en eenvoudiger te onderhouden.
Een DI-container instellen
Installeer het NuGet-pakket:
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Hosting
Configureer de host en services in uw 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();
}
}
Opmerking
Als u vergeet een service te registreren, genereert de GetService<T>() bovenstaande helper een ArgumentException at runtime met de naam van het ontbrekende type. Voer de app uit en navigeer tijdens de ontwikkeling naar elke pagina om te controleren of alle registraties juist zijn.
Afhankelijkheden in ViewModels injecteren
Als de container is geconfigureerd, ontvangen uw ViewModels afhankelijkheden via constructorinjectie:
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";
}
}
Levensduur van de service
Kies de juiste levensduur bij het registreren van services:
| Levensduur | Methode | Te gebruiken voor |
|---|---|---|
| Singleton | AddSingleton<T>() |
Navigatie, appbrede toestand, caches |
| Afgebakend | AddScoped<T>() |
Contexten per venster of per dialoogvenster |
| Transient | AddTransient<T>() |
ViewModels, statusloze services |
Tip
Registreer ViewModels als tijdelijk , zodat elke navigatie een nieuw exemplaar maakt. Registreer services die een appbrede status hebben als Singleton.
Configuratiebeheer
Gebruik Microsoft.Extensions.Configuration dit voor het beheren van app-instellingen in bureaublad-apps, hetzelfde patroon dat wordt gebruikt in ASP.NET Core.
Configuratieondersteuning toevoegen
Installeer de vereiste pakketten:
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Options
Maak een appsettings.json bestand in de hoofdmap van uw project. Klik in Solution Explorer met de rechtermuisknop op het bestand, selecteer Eigenschappen en stel Kopiëren naar uitvoermap in op Kopiëren indien nieuwer. U kunt ook <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> toevoegen aan de vermelding van het bestand in uw .csproj.
{
"AppSettings": {
"ApiBaseUrl": "https://api.contoso.com/v2",
"MaxRetryCount": 3,
"EnableTelemetry": true
},
"Logging": {
"LogLevel": "Information"
}
}
Bind de configuratie in uw DI-installatie:
.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>();
})
Configuratie gebruiken in diensten
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)
};
}
}
Persistentie van gebruikersinstellingen
Gebruik voor gebruikersspecifieke instellingen die behouden blijven na app-updates Windows.Storage.ApplicationData (ingepakte apps) of een lokaal JSON-bestand (niet-ingepakte apps):
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));
}
}
Opmerking
Verpakte apps (MSIX) kunnen ApplicationData.Current.LocalSettings gebruiken voor eenvoudige sleutel-waardeparen. Uitgepakte apps moeten hun eigen opslaglocatie beheren.
Functievlaggen
Implementeer functievlagmen om geleidelijke implementatie en A/B-tests mogelijk te maken zonder opnieuw te implementeren.
Lokale functievlagmen met configuratie
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;
}
Integratie van Azure App Configuration
Gebruik Azure App Configuration voor door de cloud beheerde functievlagmen:
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();
Opmerking
Voor lokale ontwikkeling kunt u een verbindingsreeks gebruiken in plaats van DefaultAzureCredential. Sla de verbindingsreeks op in een omgevingsvariabele of Windows Credential Manager, nooit in broncodebeheer:
options.Connect(Environment.GetEnvironmentVariable("APP_CONFIG_CONNECTION_STRING"))
Tip
Zie Gefaseerde pakketuitrol voor geleidelijke uitrol via de Store op pakketniveau.
Enterprise- en LOB-patronen
Line-Of-Business-apps hebben aanvullende vereisten voor identiteits-, gegevensbeveiliging en apparaatbeheer.
Identiteit en voorwaardelijke toegang
MSAL (Microsoft Authentication Library) gebruiken voor bedrijfsverificatie:
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
De http://localhost omleidings-URI is geschikt voor ontwikkeling. Gebruik in plaats daarvan de Windows broker (WAM) voor productie-desktop-apps, die eenmalige aanmelding biedt met het Windows-account van de gebruiker en sterkere tokenbeveiliging.
Bedrijfs-apps die zijn geïmplementeerd via Intune, kunnen beleidsregels voor voorwaardelijke toegang afdwingen waarvoor het volgende is vereist:
- Apparaatcompatibiliteit (versleuteling, pincode, versie van het besturingssysteem)
- Meervoudige verificatie
- Netwerklocatiebeperkingen
Offlinegegevens en cacheopslag
Desktop LOB-apps moeten vaak offline werken. Implementeer een opslagplaatspatroon met lokale cache:
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>();
}
}
}
Gegevensbeveiliging
Gebruik Windows.Security.Cryptography.DataProtection (verpakte apps) of de .NET DataProtectionProvider voor het versleutelen van gevoelige lokale gegevens.
Installeer het vereiste pakket:
dotnet add package Microsoft.AspNetCore.DataProtection.Extensions
Registreer vervolgens gegevensbeveiliging in uw DI-container:
using Microsoft.AspNetCore.DataProtection;
services.AddDataProtection()
.SetApplicationName("Contoso.LOBApp")
.ProtectKeysWithDpapi();
Gelaagde architectuur
Structureer uw WinUI 3-app in lagen om afhankelijkheden in één richting te laten stromen:
┌─────────────────────────────┐
│ 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
└─────────────────────────────┘
Reglement:
- Elke laag is alleen afhankelijk van de laag direct eronder.
- ViewModels verwijzen nooit naar UI-typen (
Page,Window,ContentDialog). - Services definiëren interfaces; implementaties leven in de gegevenslaag.
- Registreer alle afhankelijkheden tussen lagen in de DI-container.
Compatibiliteit met eerdere versies en versiebeheer
Wanneer u nieuwe versies van uw app vrijgeeft, kunt u het volgende overwegen:
- Gegevensmigratie: versie van uw lokale databaseschema. Gebruik een migratierunner bij het opstarten om een upgrade uit te voeren van een eerder schema naar het huidige schema.
- Migratie van instellingen: Sla een schemaversie op in uw instellingenbestand. Bij het laden transformaties van oude formaten naar nieuwe toepassen.
- Installaties naast elkaar: MSIX werkt standaard een pakket bij. Als u meerdere primaire versies naast elkaar wilt uitvoeren, wijst u elke versie een afzonderlijke pakketfamilienaam toe tijdens het ontwerp.
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);
}
}
Uw installatie controleren
Start de app en navigeer naar elke pagina om te controleren of services correct worden opgelost. Als een service niet is geregistreerd, ziet u tijdens de uitvoering een InvalidOperationException waarin de naam van het ontbrekende type wordt vermeld. Controleer ook of:
- Configuratiewaarden worden geladen vanuit
appsettings.json(controleer een gebonden eigenschap in de debugger). - Functievlagmen evalueren zoals verwacht (een vlag in- en uitschakelen en opnieuw opstarten).
- Offlinecaching retourneert gegevens wanneer het netwerk niet beschikbaar is.
Verwante onderwerpen
Windows developer