Sdílet prostřednictvím


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.

Vzory podnikových aplikací pomocí úvodní miniatury eBooku .NET MAUI

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.DependencyInjectioninjektáži závislostí v .NET.

V rozhraní .NET MAUItřída MauiProgram zavolá metodu CreateMauiApp k vytvoření objektu MauiAppBuilder . Objekt MauiAppBuilderServices 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 IAppEnvironmentServicevyřešena rozhraní , IDialogServiceINavigationService, 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 RegisterAppServicesrozšíření , RegisterViewModelsa 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 MauiAppBuildera 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 Buildse 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í:

  1. Pokud typ není zaregistrovaný, kontejner vyvolá výjimku.
  2. 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.
  3. 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ší.