36
DI v .NET bez pověr, iluzí a frikulínského nadšení René Stein http://renestein.net http://twitter.com/ renestein MS FEST 2012

Dependency injection v .Net Frameworku

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: Dependency injection v .Net Frameworku

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

René Steinhttp://renestein.net

http://twitter.com/renestein

MS FEST 2012

Page 2: Dependency injection v .Net Frameworku

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

Page 3: Dependency injection v .Net Frameworku

Pracujeme se závislostmi - v aplikacích

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

Page 4: Dependency injection v .Net Frameworku

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í.

Page 5: Dependency injection v .Net Frameworku

Singleton v aplikaci

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

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

Page 6: Dependency injection v .Net Frameworku

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.

Page 7: Dependency injection v .Net Frameworku

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

v aplikaci na jeden singleton

Page 8: Dependency injection v .Net Frameworku

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.

Page 9: Dependency injection v .Net Frameworku

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.

Page 10: Dependency injection v .Net Frameworku

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(….); }

Page 11: Dependency injection v .Net Frameworku

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)

Page 12: Dependency injection v .Net Frameworku

Injektování přes konstruktor

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

Page 13: Dependency injection v .Net Frameworku

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

Page 14: Dependency injection v .Net Frameworku

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

public IConfigService ConfigService {

get; set;

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

}

Page 15: Dependency injection v .Net Frameworku

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())

Page 16: Dependency injection v .Net Frameworku

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; }

Page 17: Dependency injection v .Net Frameworku

Injektování přes argument metody

public void RunTransition (IWorkflowContext context)

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

this);}

Page 18: Dependency injection v .Net Frameworku

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

Page 19: Dependency injection v .Net Frameworku

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.

Page 20: Dependency injection v .Net Frameworku

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)

Page 21: Dependency injection v .Net Frameworku

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.

Page 22: Dependency injection v .Net Frameworku

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“.

Page 23: Dependency injection v .Net Frameworku

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

CommonServices" type="DefaultServices.DbLogger,

DefaultServices" </component></components>

Page 24: Dependency injection v .Net Frameworku

R = Registrace - další doporučení

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

Page 25: Dependency injection v .Net Frameworku

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());

Page 26: Dependency injection v .Net Frameworku

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:

Page 27: Dependency injection v .Net Frameworku

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).

Page 28: Dependency injection v .Net Frameworku

DI kontajner není service locator!

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

container.Resolve<IChildView>;

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

Page 29: Dependency injection v .Net Frameworku

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>());

Page 30: Dependency injection v .Net Frameworku

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.

Page 31: Dependency injection v .Net Frameworku

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

Page 32: Dependency injection v .Net Frameworku

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?

Page 33: Dependency injection v .Net Frameworku

DI kontajnery v .Net světě

StructureMap

Castle Windsor Hiro

Spring.NET

Autofac Unity

Ninject

?MEF?

Page 34: Dependency injection v .Net Frameworku

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…

Page 35: Dependency injection v .Net Frameworku
Page 36: Dependency injection v .Net Frameworku

René Stein

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