Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ez a cikk bemutatja, hogyan alkalmazhat bevált architektúramintákat a Windows App SDK készült WinUI 3 asztali alkalmazásokra. Megtudhatja, hogyan állíthat be függőséginjektálást, kezelheti a konfigurációt és a vállalati üzletági (LOB) forgatókönyvekhez tartozó struktúrakódot.
Prerequisites
- Windows App SDK 1,5-ös vagy újabb verzió
- .NET 8 vagy újabb
- Visual Studio 2022 17.10-es vagy újabb verziója a .NET asztali fejlesztés és a Windows-alkalmazásfejlesztés munkaterhelésekkel
Függőséginjektálás
A WinUI 3-alapú asztali alkalmazások nem tartalmaznak az ASP.NET Core-hoz hasonló beépített függőséginjektálási (DI-)tárolót, de ugyanazzal a Microsoft.Extensions.DependencyInjection NuGet-csomaggal hozzáadhat egyet. A DI tesztelhetővé, lazán összekapcsolhatóvá és könnyebben karbantarthatóvá teszi a kódot.
DI-tároló beállítása
Telepítse a NuGet-csomagot:
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Hosting
Konfigurálja a gazdagépet és a szolgáltatásokat a(z) App.xaml.cs elemben:
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();
}
}
Megjegyzés:
Ha elfelejt regisztrálni egy szolgáltatást, a GetService<T>() fenti segítő futásidőben dob egy ArgumentException hiányzó típusnevet. Futtassa az alkalmazást, és keresse meg az egyes lapokat a fejlesztés során, és ellenőrizze, hogy az összes regisztráció helyes-e.
Függőségek injektálása a ViewModelsbe
A tároló konfigurálásával a ViewModels konstruktorinjektáláson keresztül függőségeket kap:
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";
}
}
Szolgáltatási élettartamok
Válassza ki a megfelelő élettartamot a szolgáltatások regisztrálásakor:
| Lifetime | Módszer | Felhasználási területek: |
|---|---|---|
| Singleton | AddSingleton<T>() |
Navigáció, alkalmazásszintű állapot, gyorsítótárak |
| Hatókör | AddScoped<T>() |
Ablakonkénti vagy párbeszédpanelenkénti környezetek |
| Transient | AddTransient<T>() |
ViewModels, állapot nélküli szolgáltatások |
Jótanács
Regisztrálja a ViewModelleket Transientként, hogy minden navigáláskor új példány jöjjön létre. Regisztrálja Singleton-ként azokat a szolgáltatásokat, amelyek az alkalmazás egészére kiterjedő állapotot tárolnak.
Konfigurációkezelés
A(z) Microsoft.Extensions.Configuration használatával kezelheti az alkalmazásbeállításokat az asztali alkalmazásokban, ugyanazzal a mintával, amelyet az ASP.NET Core is használ.
Konfigurációs támogatás hozzáadása
Telepítse a szükséges csomagokat:
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Options
Hozzon létre egy appsettings.json fájlt a projekt gyökérkönyvtárában. A Megoldáskezelőben kattintson a jobb gombbal a fájlra, válassza a Tulajdonságok lehetőséget, majd állítsa a Másolás a kimeneti könyvtárba értékét erre: Másolás, ha újabb. Másik lehetőségként adja hozzá a(z) <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> elemet a fájl bejegyzéséhez a(z) .csproj fájlban.
{
"AppSettings": {
"ApiBaseUrl": "https://api.contoso.com/v2",
"MaxRetryCount": 3,
"EnableTelemetry": true
},
"Logging": {
"LogLevel": "Information"
}
}
Kösd össze a konfigurációt a DI-beállításban:
.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>();
})
Konfiguráció használata a szolgáltatásokban
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)
};
}
}
Felhasználói beállítások megőrzése
Az alkalmazásfrissítéseket túlélő felhasználónkénti beállításokhoz használja Windows.Storage.ApplicationData a (csomagolt alkalmazásokat) vagy egy helyi JSON-fájlt (kicsomagolt alkalmazások):
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));
}
}
Megjegyzés:
A csomagolt alkalmazások (MSIX) egyszerű kulcs-érték párokhoz használhatók ApplicationData.Current.LocalSettings . A csomagolatlan alkalmazásoknak saját tárhelyet kell kezelniük.
Funkciójelzők
A funkciójelzők implementálása a fokozatos bevezetés és az A/B tesztelés újbóli üzembe helyezés nélküli engedélyezéséhez.
Helyi funkciójelzők konfigurációval
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;
}
Az Azure App Configuration integrációja
A felhőben felügyelt funkciójelzőkhöz használja az Azure App Configurationt:
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();
Megjegyzés:
A helyi fejlesztéshez a DefaultAzureCredential helyett kapcsolati sztringet használhat. A kapcsolati karakterláncot környezeti változóban vagy a Windows Hitelesítőadat-kezelőben tárolja – soha ne a verziókezelőben:
options.Connect(Environment.GetEnvironmentVariable("APP_CONFIG_CONNECTION_STRING"))
Jótanács
Az Áruház-alapú fokozatos bevezetést a csomag szintjén a fokozatos bevezetés című témakörben találhatja meg.
Vállalati és LOB-minták
Az üzletági alkalmazások további követelményeket támasztanak az identitással, az adatvédelemmel és az eszközkezeléssel szemben.
Identitás és feltételes hozzáférés
Az MSAL (Microsoft Authentication Library) használata vállalati hitelesítéshez:
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
Az http://localhost átirányítási URI fejlesztésre alkalmas. Éles használatra szánt asztali alkalmazásokhoz inkább a Windows-közvetítőt (WAM) használja, amely a felhasználó Windows-fiókjával való egyszeri bejelentkezést, valamint erősebb jogkivonat-védelmet biztosít.
Az Intune-on keresztül üzembe helyezett vállalati alkalmazások az alábbiakat igénylő feltételes hozzáférési szabályzatokat kényszeríthetnek ki:
- Eszközmegfelelés (titkosítás, PIN-kód, operációs rendszer verziója)
- Többtényezős hitelesítés
- Hálózati helykorlátozások
Offline adatok és gyorsítótárazás
Az asztali LOB-alkalmazásoknak gyakran offline állapotban kell működnie. Tárházminta implementálása helyi gyorsítótárazással:
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>();
}
}
}
Adatvédelem
Bizalmas helyi adatok titkosításához használja Windows.Security.Cryptography.DataProtection a (csomagolt alkalmazásokat) vagy a .NETDataProtectionProvider.
Telepítse a szükséges csomagot:
dotnet add package Microsoft.AspNetCore.DataProtection.Extensions
Ezután regisztrálja az adatvédelmet a DI-tárolóban:
using Microsoft.AspNetCore.DataProtection;
services.AddDataProtection()
.SetApplicationName("Contoso.LOBApp")
.ProtectKeysWithDpapi();
Rétegzett architektúra
Strukturálja a WinUI 3 alkalmazást rétegekbe, hogy a függőségek egy irányba haladjanak:
┌─────────────────────────────┐
│ 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
└─────────────────────────────┘
Szabályok:
- Minden réteg csak az alatta lévő rétegtől függ.
- A ViewModels soha nem hivatkozik felhasználói felületi típusokra (
Page,Window, ).ContentDialog - A szolgáltatások interfészeket definiálnak; az implementációk az adatrétegben élnek.
- Regisztrálja az összes rétegközi függőséget a DI-tárolóban.
Visszamenőleges kompatibilitás és verziószámozás
Az alkalmazás új verzióinak kiadásakor vegye figyelembe a következő szempontokat:
- Adatmigrálás: A helyi adatbázisséma verziója. Az indításkor egy migrálási futó használatával frissítsen az előző sémáról az aktuálisra.
- Beállítások migrálása: A sémaverzió tárolása a beállításfájlban. Betöltés esetén alkalmazza az átalakításokat a régi formátumokról az újakra.
- Egymás melletti telepítések: Az MSIX alapértelmezés szerint frissíti a csomagokat. Ha több főverziót szeretne egymás mellett futtatni, minden verzióhoz rendeljen külön csomagcsaládnevet a tervezéskor.
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);
}
}
A beállítás ellenőrzése
Futtassa az alkalmazást, és navigáljon az egyes oldalakra a szolgáltatások helyes feloldásának ellenőrzéséhez. Ha egy szolgáltatás nincs regisztrálva, futásidőben egy InvalidOperationException jelenik meg, amely tartalmazza a hiányzó típus nevét. Győződjön meg arról is, hogy:
- A konfigurációs értékek a(z)
appsettings.jsonelemből töltődnek be (ellenőrizzen egy kötött tulajdonságot a hibakeresőben). - A funkciókapcsolók az elvártaknak megfelelően működnek (kapcsoljon át egy jelölőt, majd indítsa újra).
- Az offline gyorsítótárazás akkor ad vissza adatokat, ha a hálózat nem érhető el.
Kapcsolódó tartalom
Windows developer