Dependency injection v .Net Frameworku

Preview:

DESCRIPTION

Dependency injection v .Net bez pověr, iluzí a frikulínského nadšení.

Citation preview

DI v .NET bez pověr, iluzí a frikulínského nadšení

René Steinhttp://renestein.net

http://twitter.com/renestein

MS FEST 2012

Na této přednášce nebude pomlouváno Metro (ani kdyby se jmenovalo Modern UI)!

Pracujeme se závislostmi - v aplikacích

• A rovnou se na takovou aplikaci podíváme (bez pomoci Karla Nešpora)

Každý problém se závislostmi vyřeší on!

Náš chrabrý Singleton!

Bůh je stejně jako singleton vždy jen jeden. Skeptická poznámka: dokud nejsou

oba minimálně trojjediní.

Singleton v aplikaci

• Ukázka singletonu• Ukázka komplikovanějšího singletonu

složeného z dalších singletonů – SimpleServiceHolder.

Singleton(y)Poskytuje globální přístupový bod k

jedné instanci. Ale:• Skryté závislosti v kódu aplikace.• Malá kontrola nad životním cyklem

singletonu (jen ta „lazy“ inicializace).• Problémy se singletony ve

vícevláknovém prostředí.• Problémy se singletony v unit

testech.

Repozitář služeb• Vulgo „service locator“• Nabízí „služby všeho druhu“ • Redukuje větší množství singletonů

v aplikaci na jeden singleton

Repozitář služeb - nevýhody

Objekt, který ve svém rozhraní slibuje víc, než může splnit.

Ve svém rozhraní nabízí všechny služby na světě (a možná i mimo něj).

Závislosti třídy nepoznáme z jejího rozhraní, protože komunikace s repozitářem služeb je utopena v privátní implementaci třídy.

Repozitář služeb – nevýhody II

Nepřehledný kód SimpleServiceRepository.Instance.GetService<X> - to je všudypřítomná náhrada za volání konstruktoru - new X().

SimpleServiceRepository.Instance.GetService< ….>

• Všechny knihovny většinouzávisí na knihovně s SimpleServiceRepository.

Repozitář služeb – nevýhody III

• Nikdy se nemůžete spolehnout na to, že služba je repozitáři registrována.

• Víte, co je „Ambient context“? var errService =

SimpleServiceRepository.Instance.GetService<IWorkflowErrorInfoProviderEx>(); - na jiném místě

if(errService != null) { errService.PublishError(….); }

Dependency injection

Odstranění skrytých závislostí

Odstranění těsných vazeb mezi objekty

(Snadné) mapování abstrakcí na

implementaci

Spolupráce objektů s přesně vymezenou odpovědností (SRP)

Injektování přes konstruktor

var myOrderService = new OrderService (myRepository);class OrderService{ public OrderService(IRepository<Order> repository) { if(repository == null) { throw ….; } m_repository = repository; }….}

Injektujeme přes konstruktor

Předávání závislostí, bez kterých instance nemůže existovat

Preferujte předávání závislostí přes konstruktor

Injektujeme přes vlastnostpublic class ExportService{ public ExportService() { ConfigService = new DefaultConfigService(); }

public IConfigService ConfigService {

get; set;

} public void ExportData { …. ConfigService.GetValue(…);}

}

Kdy injektovat přes vlastnost?

Když je závislost nepovinná. Ale pozor na NullReferenceException při používání závislosti!

Když máte smysluplnou výchozí implementaci (i Null object) => ve třídě se ale svážeme s další konkrétní třídou (new (konkrétní) ZavislostX())

Pozor na problémy s automatickým injektováním

závislostí přes vlastnosti

interface IViewModel { IView CurrentView { get; } }

//ViewModelBase [DoNotWire]public IView CurrentView { get; set; }

Injektování přes argument metody

public void RunTransition (IWorkflowContext context)

{ if(context == null) { throw ….; } …context.ApplyTransitionRules(currentState,

this);}

Kdy injektovat přes argument metody

Při každém volání metody potřebujeme odlišný objekt

Předávaný objekt většinou představuje unikátní kontext jednoho volání metody

S tímto druhem závislostí nám DI kontajner nepomůže

DI kontajner

DI kontajner si představme jako nástroj, který nám zjednodušuje vytváření objektových grafů.

Jako nástroj, pomocí kterého snadno propojíme nezávislé části naší aplikace a splníme jim jejich přání = dodáme jim všechny jejich závislosti.

Co očekávat od DI kontajneru?

SOLIDní kompozice

objektů

Správa životního cyklu služeb, resp. komponent

(Singleton, PerThread, PerWebRequest,

Transient)

Intercepce metod (skvělá podpora návrhového vzoru

Decorator)

Použití DI kontajneru – 3 RRegister

R =Registrace

Jednorázová registrace abstrakcí na implementace (registrace komponent a služeb) v takzvaném „DI kompozičním centru “ (composition root) po startu aplikace.

R = registrace• Preferujte konfiguraci v kódu.

m_container.Register(Component.For(typeof(IRepository<>)) .ImplementedBy(typeof(DefaultEFRepository<>)));

• Registrace v XML konfiguračním souboru jen tehdy, když chcete přidávat „pluginy“.

Registrace v XML – Castle Windsor („pluginy – pozdní vazba “)<components> <component id= "MyLogService" service= "CommonServices.ILogger,

CommonServices" type="DefaultServices.DbLogger,

DefaultServices" </component></components>

R = Registrace - další doporučení

Preferujte hromadné registrace s využitím konvencí místo explicitní registrace každé služby

Registrace na základě jmenných konvencí

Predicate<Type> servicesCondition = type => type.Name.Contains(PROVIDER_SUFFIX) || type.Name.Contains(SERVICE_SUFFIX);

m_container.Register(Classes.FromAssembly. Containing(typeof(INewObjectIdProvider<>))

.Where(servicesCondition) .WithServiceDefaultInterfaces() .WithServiceSelf()

.LifestyleSingleton());

Použití DI kontajneru – 3 RResolve

R = Resolve

Získání služby z DI kontajneru. Službu mohu

ihned používat, její závislosti splnil DI

kontajner

ALE:

Princip neviditelnosti DI kontajneru

Nikdy nepodlehněte pokušení používat DI kontajner jako service locator (repozitář služeb).

Nikdy se ve svém aplikačním kódu nereferencujte DI kontajner. K tomu vám dopomáhej zdravý vývojářský úsudek a vzor Abstract Factory.

Metodu „Resolve“ na DI kontajneru volá jen „composition root“ a infrastrukturní objekty (ControllerFactory v ukázce).

DI kontajner není service locator!

public MainViewModel(IWindsorContainer container){ ….}var childView =

container.Resolve<IChildView>;

public MainViewModel(IViewFactory viewfactory){…. }var childView =viewfactory.Create<IChildView>();

Abstraktní továrna ve Windsor Castlu

• Castle dokáže typické abstraktní továrny generovat sám s využitím dynamické proxy.

m_container.AddFacility<TypedFactoryFacility>();

m_container.Register(Component.For<IViewFactory>().AsFactory<IViewFactory>());

Použití DI kontajneru – 3 RRelease

• Cokoli vyzvednu „manuálně“ z DI kontajneru (Resolve) uvolním metodou Release.

public override void ReleaseController(IController controller)

{ m_container.Release(controller); base.ReleaseController(controller); }• Životní cyklus služeb řídí DI kontajner.

Mýty o dependency injection (a IoC)DI je komplikovaná záležitost .(A já jsem duše prostá

)

Nepíšu unit testy (!) => nepotřebuju DI

Kdo nepoužívá DI (nebo o ní alespoň nemluví) není pravý vývojář

DI == DI kontajner

Říká se tomu DI kontajner, ale já vím, že je to jen vylepšená abstraktní továrna

Mýty o dependency injection

• V konstruktoru se mi budou množit argumenty => jednoduchý detektor porušení SRP.

public GodService(IWorldBuilder worldbuilder, IHelloWorld initWorldService,IEdenService edenService,IJesusChristSupport jesusSupportServiceIApocalypseRunner apocalypseRunner)

• Víte, co jsou kompozitní služby?

DI kontajnery v .Net světě

StructureMap

Castle Windsor Hiro

Spring.NET

Autofac Unity

Ninject

?MEF?

DI kontajnery – podobnosti a rozdíly

Svatou DI trojici Register/Resolve/Release zvládne každý DI kontajner.

Pak má ale každý kontajner své unikátní vlastnosti –Windsor Castle vyniká při intercepci pomocí dynamické proxy nebo se s jeho pomocí skvěle diagnostikují chyby při registraci služeb…

René Stein

• Vývoj aplikací, veřejné a inhouse kurzy• http://www.renestein.net/nabidka.aspxhttp://blog.renestein.nethttp://twitter.com/renestein

Recommended