Injektáž závislostí
Tip
Tento obsah je výňatek z elektronické knihy, vzory podnikových aplikací pomocí .NET MAUI, dostupné na .NET Docs nebo jako zdarma ke stažení PDF, které lze číst offline.
Konstruktor třídy se obvykle vyvolá při vytváření instance objektu a všechny hodnoty, které objekt potřebuje, jsou předány jako argumenty konstruktoru. Toto je příklad injektáž závislostí označované jako injektáž konstruktoru. Závislosti, které objekt potřebuje, se vloží do konstruktoru.
Zadáním závislostí jako typů rozhraní umožňuje injektáž závislostí oddělit konkrétní typy od kódu, který závisí na těchto typech. Obvykle používá kontejner, který obsahuje seznam registrací a mapování mezi rozhraními a abstraktními typy a konkrétní typy, které implementují nebo rozšiřují tyto typy.
Existují také další typy injektáže závislostí, jako je injektáž setter vlastností a injektáž volání metody, ale jsou méně často vidět. Proto se tato kapitola zaměří výhradně na provádění injektáže konstruktoru s kontejnerem injektáže závislostí.
Úvod do injektáže závislostí
Injektáž závislostí je specializovaná verze modelu Inversion of Control (IoC), kde problém invertovaný je proces získání požadované závislosti. Při injektáži závislostí zodpovídá jiná třída za vkládání závislostí do objektu za běhu. Následující příklad kódu ukazuje, jak ProfileViewModel
je třída strukturovaná při použití injektáže závislostí:
private readonly ISettingsService _settingsService;
private readonly IAppEnvironmentService _appEnvironmentService;
public ProfileViewModel(
IAppEnvironmentService appEnvironmentService,
IDialogService dialogService,
INavigationService navigationService,
ISettingsService settingsService)
: base(dialogService, navigationService, settingsService)
{
_appEnvironmentService = appEnvironmentService;
_settingsService = settingsService;
// Omitted for brevity
}
Konstruktor ProfileViewModel
přijímá více instancí objektu rozhraní jako argumenty vložené jinou třídou. Jediná závislost ve ProfileViewModel
třídě je na typech rozhraní. ProfileViewModel
Třída proto nemá žádné znalosti třídy, která je zodpovědná za vytvoření instance objektů rozhraní. Třída, která je zodpovědná za vytvoření instance objektů rozhraní a její vložení do ProfileViewModel
třídy, se označuje jako kontejner injektáž závislostí.
Kontejnery injektáže závislostí snižují spojení mezi objekty tím, že poskytují zařízení pro vytvoření instance instancí tříd a správu jejich životnosti na základě konfigurace kontejneru. Během vytváření objektu kontejner vloží všechny závislosti, které do objektu vyžaduje. Pokud se tyto závislosti ještě nevytvořily, kontejner nejprve vytvoří a přeloží jejich závislosti.
Použití kontejneru injektáže závislostí má několik výhod:
- Kontejner eliminuje potřebu třídy vyhledat její závislosti a spravovat jeho životnost.
- Kontejner umožňuje mapování implementovaných závislostí, aniž by to ovlivnilo třídu.
- Kontejner usnadňuje testovatelnost tím, že umožňuje napodobení závislostí.
- Kontejner zvyšuje udržovatelnost tím, že umožňuje snadné přidání nových tříd do aplikace.
V kontextu aplikace .NET MAUI , která používá MVVM, se kontejner injektáže závislostí obvykle použije k registraci a překladu zobrazení, registraci a překladu modelů zobrazení a k registraci služeb a jejich vkládání do zobrazení modelů.
V .NET je k dispozici mnoho kontejnerů injektáže závislostí; multiformní aplikace eShop používá Microsoft.Extensions.DependencyInjection
ke správě vytváření instancí zobrazení, zobrazení modelů a tříd služeb v aplikaci. Microsoft.Extensions.DependencyInjection
usnadňuje vytváření volně propojených aplikací a poskytuje všechny funkce, které se běžně vyskytují v kontejnerech injektáže závislostí, včetně metod registrace mapování typů a instancí objektů, překladu objektů, správy životností objektů a vkládání závislých objektů do konstruktorů objektů, které řeší. Další informace o Microsoft.Extensions.DependencyInjection
injektáži závislostí v .NET.
V rozhraní .NET MAUItřída MauiProgram
zavolá metodu CreateMauiApp
k vytvoření objektu MauiAppBuilder
. Objekt MauiAppBuilder
má Services
vlastnost typu IServiceCollection
, která poskytuje místo pro registraci našich komponent, jako jsou zobrazení, zobrazení modelů a služby pro injektáž závislostí. Při volání metody se do kontejneru MauiAppBuilder.Build
injektáže závislostí poskytnou všechny komponenty zaregistrované ve Services
vlastnosti.
Za běhu musí kontejner vědět, o jakou implementaci služeb se žádá, aby bylo možné vytvořit instanci požadovaných objektů. V aplikaci eShop pro více platforem musí být před vytvořením instance objektu IAppEnvironmentService
vyřešena rozhraní , IDialogService
INavigationService
, a ISettingsService
ProfileViewModel
rozhraní. To zahrnuje kontejner, který provádí následující akce:
- Rozhodování o vytvoření instance objektu, který implementuje rozhraní. To se označuje jako registrace.
- Vytvoření instance objektu, který implementuje požadované rozhraní a
ProfileViewModel
objekt. To se označuje jako řešení.
Aplikace nakonec dokončí používání objektu ProfileViewModel
a bude k dispozici pro uvolňování paměti. V tomto okamžiku by systém uvolňování paměti měl likvidovat jakékoli krátkodobé implementace rozhraní, pokud jiné třídy nesdílely stejnou instanci.
Registrace
Před vložením závislostí do objektu musí být nejprve zaregistrovány typy závislostí v kontejneru. Registrace typu zahrnuje předání kontejneru rozhraní a konkrétního typu, který implementuje rozhraní.
Existují dva způsoby registrace typů a objektů v kontejneru prostřednictvím kódu:
- Zaregistrujte typ nebo mapování v kontejneru. Tato registrace se označuje jako přechodná registrace. V případě potřeby kontejner sestaví instanci zadaného typu.
- Zaregistrujte existující objekt v kontejneru jako singleton. V případě potřeby kontejner vrátí odkaz na existující objekt.
Poznámka:
Kontejnery injektáže závislostí nejsou vždy vhodné. Injektáž závislostí přináší další složitost a požadavky, které nemusí být vhodné nebo užitečné pro malé aplikace. Pokud třída nemá žádné závislosti nebo není závislostí pro jiné typy, nemusí mít smysl ji vložit do kontejneru. Kromě toho, pokud třída má jednu sadu závislostí, které jsou integrální pro typ a nikdy se nezmění, nemusí mít smysl jej vložit do kontejneru.
Registrace typů vyžadujících injektáž závislostí by se měla provádět v jedné metodě v aplikaci. Tato metoda by se měla vyvolat v rané fázi životního cyklu aplikace, aby se zajistilo, že bude vědět o závislostech mezi jeho třídami. Tuto metodu MauiProgram.CreateMauiApp
provádí multiformní aplikace eShop. Následující příklad kódu ukazuje, jak aplikace eShop pro více platforem deklaruje CreateMauiApp
ve MauiProgram
třídě:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
=> MauiApp.CreateBuilder()
.UseMauiApp<App>()
// Omitted for brevity
.RegisterAppServices()
.RegisterViewModels()
.RegisterViews()
.Build();
}
Metoda MauiApp.CreateBuilder
vytvoří MauiAppBuilder
objekt, který můžeme použít k registraci závislostí. Mnoho závislostí v aplikaci pro více platforem eShopu je potřeba zaregistrovat, takže metody RegisterAppServices
rozšíření , RegisterViewModels
a RegisterViews
byly vytvořeny, aby pomohly poskytnout uspořádaný a udržovatelný pracovní postup registrace. Následující kód ukazuje metodu RegisterViewModels
:
public static MauiAppBuilder RegisterViewModels(this MauiAppBuilder mauiAppBuilder)
{
mauiAppBuilder.Services.AddSingleton<ViewModels.MainViewModel>();
mauiAppBuilder.Services.AddSingleton<ViewModels.LoginViewModel>();
mauiAppBuilder.Services.AddSingleton<ViewModels.BasketViewModel>();
mauiAppBuilder.Services.AddSingleton<ViewModels.CatalogViewModel>();
mauiAppBuilder.Services.AddSingleton<ViewModels.ProfileViewModel>();
mauiAppBuilder.Services.AddTransient<ViewModels.CheckoutViewModel>();
mauiAppBuilder.Services.AddTransient<ViewModels.OrderDetailViewModel>();
mauiAppBuilder.Services.AddTransient<ViewModels.SettingsViewModel>();
mauiAppBuilder.Services.AddTransient<ViewModels.CampaignViewModel>();
mauiAppBuilder.Services.AddTransient<ViewModels.CampaignDetailsViewModel>();
return mauiAppBuilder;
}
Tato metoda obdrží instanci MauiAppBuilder
a můžeme použít Services
vlastnost k registraci našich modelů zobrazení. V závislosti na potřebách vaší aplikace možná budete muset přidat služby s různými životnostmi. Následující tabulka obsahuje informace o tom, kdy můžete chtít zvolit tyto různé životnosti registrace:
metoda | Popis |
---|---|
AddSingleton<T> |
Vytvoří jednu instanci objektu, která zůstane po celou dobu životnosti aplikace. |
AddTransient<T> |
Při požadavku během řešení vytvoří novou instanci objektu. Přechodné objekty nemají předdefinované životnosti, ale obvykle se řídí životností svého hostitele. |
Poznámka:
Modely zobrazení nedědí z rozhraní, takže potřebují pouze konkrétní typ zadaný pro metody AddSingleton<T>
a AddTransient<T>
metody.
Používá se CatalogViewModel
v blízkosti kořenového adresáře aplikace a měl by být vždy dostupný, takže jeho AddSingleton<T>
registrace je výhodná. Jiné modely zobrazení, jako CheckoutViewModel
OrderDetailViewModel
jsou a jsou situační přechod na nebo jsou použity později v aplikaci. Předpokládejme, že víte, že máte komponentu, která nemusí být vždy použita. V takovém případě, pokud je paměť nebo výpočetně náročná nebo vyžaduje data za běhu, může být vhodnějším kandidátem na AddTransient<T>
registraci.
Dalším běžným způsobem, jak přidat služby, je použití AddSingleton<TService, TImplementation>
a AddTransient<TService, TImplementation>
metod. Tyto metody mají dva vstupní typy: definici rozhraní a konkrétní implementaci. Tento typ registrace je nejvhodnější pro případy, kdy implementujete služby založené na rozhraních. V následujícím příkladu kódu zaregistrujeme naše ISettingsService
rozhraní pomocí SettingsService
implementace:
public static MauiAppBuilder RegisterAppServices(this MauiAppBuilder mauiAppBuilder)
{
mauiAppBuilder.Services.AddSingleton<ISettingsService, SettingsService>();
// Omitted for brevity...
}
Po registraci MauiAppBuilder.Build
všech služeb by se měla volat metoda pro vytvoření kontejneru MauiApp
injektáže závislostí a naplnění kontejneru injektáže závislostí všemi registrovanými službami.
Důležité
Build
Po zavolání metody je kontejner injektáže závislostí neměnný a už se nedá aktualizovat ani upravit. Před voláním Build
se ujistěte, že všechny služby, které potřebujete v rámci aplikace, byly zaregistrovány .
Rozlišení
Jakmile je typ zaregistrovaný, lze ho vyřešit nebo vkládat jako závislost. Při překladu typu a kontejner musí vytvořit novou instanci, vloží do instance všechny závislosti.
Obecně platí, že když se typ vyřeší, stane se jedna ze tří věcí:
- Pokud typ není zaregistrovaný, kontejner vyvolá výjimku.
- Pokud byl typ registrován jako singleton, vrátí kontejner instanci singleton. Pokud se jedná o první volání typu, kontejner ho v případě potřeby vytvoří a udržuje odkaz na něj.
- Pokud byl typ registrován jako přechodný, kontejner vrátí novou instanci a neudržuje odkaz na něj.
.NET MAUI nabízí řadu způsobů řešení registrovaných komponent na základě vašich potřeb. Nejpřímější způsob, jak získat přístup ke kontejneru injektáže závislostí, je použít Element
Handler.MauiContext.Services
. Příklad je uvedený níže:
var settingsService = this.Handler.MauiContext.Services.GetServices<ISettingsService>();
To může být užitečné, pokud potřebujete vyřešit službu z nějakého Element
konstruktoru nebo mimo jeho konstruktor Element
.
Upozornění
Existuje možnost, že Handler
vlastnost vašeho Element
může být null, takže mějte na paměti, že možná budete muset tyto situace zpracovat. Další informace najdete v tématu Životní cyklus obslužné rutiny v Centru dokumentace Společnosti Microsoft.
Pokud používáte Shell
ovládací prvek pro .NET MAUI, implicitně zavolá do kontejneru injektáž závislostí k vytvoření objektů během navigace. Při nastavování našeho Shell
ovládacího prvku Routing.RegisterRoute
metoda prováže trasu na trasu View
, jak je znázorněno v následujícím příkladu:
Routing.RegisterRoute("Filter", typeof(FiltersView));
Během Shell
navigace vyhledá registrace objektu FiltersView
, a pokud jsou nalezeny, vytvoří toto zobrazení a vloží všechny závislosti do konstruktoru. Jak je znázorněno v následujícím příkladu kódu, CatalogViewModel
vloží se do FiltersView
:
namespace eShop.Views;
public partial class FiltersView : ContentPage
{
public FiltersView(CatalogViewModel viewModel)
{
BindingContext = viewModel;
InitializeComponent();
}
}
Tip
Kontejner injektáže závislostí je skvělý pro vytváření instancí modelu zobrazení. Pokud model zobrazení obsahuje závislosti, zpracuje vytváření a injektáž všech požadovaných služeb. Jen se ujistěte, že zaregistrujete modely zobrazení a všechny závislosti, které mohou mít s metodou CreateMauiApp
MauiProgram
ve třídě.
Shrnutí
Injektáž závislostí umožňuje oddělení konkrétních typů od kódu, který závisí na těchto typech. Obvykle používá kontejner, který obsahuje seznam registrací a mapování mezi rozhraními a abstraktními typy a konkrétní typy, které implementují nebo rozšiřují tyto typy.
Microsoft.Extensions.DependencyInjection
usnadňuje sestavování volně propojených aplikací a poskytuje všechny funkce běžně používané v kontejnerech injektáže závislostí, včetně metod registrace mapování typů a instancí objektů, řešení objektů, správy životností objektů a vkládání závislých objektů do konstruktorů objektů, které řeší.