1. MS FEST 2012
DI v .NET bez pověr, iluzí a
frikulínského nadšení
René Stein
http://renestein.net
http://twitter.com/renestein
2. Na této přednášce nebude pomlouváno
Metro (ani kdyby se jmenovalo Modern UI)!
3. Pracujeme se závislostmi - v
aplikacích
• A rovnou se na takovou aplikaci
podíváme (bez pomoci Karla
Nešpora)
4. 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í.
5. Singleton v aplikaci
• Ukázka singletonu
• Ukázka komplikovanějšího singletonu
složeného z dalších singletonů –
SimpleServiceHolder.
6. 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.
7. Repozitář služeb
• Vulgo „service locator“
• Nabízí „služby všeho druhu“
• Redukuje větší množství singletonů
v aplikaci na jeden singleton
8. 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.
9. Repozitář služeb – nevýhody II
Nepřehledný kód
SimpleServiceRepository.Instance.GetSer
vice<X> - to je všudypřítomná náhrada
za volání konstruktoru - new X().
SimpleServiceRepository.Instance.GetServic
e< ….>
• Všechny knihovny většinou
závisí na knihovně s
SimpleServiceRepository.
10. 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<IWorkflowE
rrorInfoProviderEx>(); - na jiném místě
if(errService != null)
{
errService.PublishError(….);
}
11. Dependency injection
Odstranění skrytých Odstranění těsných
závislostí vazeb mezi objekty
(Snadné) mapování Spolupráce objektů s
abstrakcí na přesně vymezenou
implementaci odpovědností (SRP)
12. Injektování přes
konstruktor
var myOrderService = new OrderService (myRepository);
class OrderService
{
public OrderService(IRepository<Order> repository)
{
if(repository == null)
{
throw ….;
}
m_repository = repository;
}
….
}
14. Injektujeme přes vlastnost
public class ExportService
{
public ExportService()
{
ConfigService = new DefaultConfigService();
}
public IConfigService ConfigService
{
get;
set;
}
public void ExportData
{
….
ConfigService.GetValue(…);
}
}
15. 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())
16. Pozor na problémy s
automatickým injektováním
závislostí přes vlastnosti
//ViewModelBase
interface IViewModel
{ [DoNotWire]
IView CurrentView public IView
{ CurrentView
get; {
} get;
} set;
}
17. Injektování přes argument
metody
public void RunTransition
(IWorkflowContext context)
{
if(context == null)
{
throw ….;
}
…
context.ApplyTransitionRules(currentState,
this);
18. 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
19. 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.
20. Co očekávat od DI
kontajneru?
SOLIDní Správa životního cyklu
služeb, resp.
kompozice komponent
objektů (Singleton, PerThread,
PerWebRequest, Transi
ent)
Intercepce metod (skvělá
podpora návrhového vzoru
Decorator)
21. Použití DI kontajneru – 3 R
Register
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.
22. 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“.
24. R = Registrace - další
doporučení
Preferujte hromadné
registrace s využitím
konvencí místo
explicitní registrace
každé služby
25. 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());
26. Použití DI kontajneru – 3 R
Resolve
R = Resolve
Získání služby z DI
kontajneru. Službu mohu
ihned používat, její
závislosti splnil DI
kontajner
ALE:
27. Princip neviditelnosti DI
kontajneru
Nikdy nepodlehněte pokušení používat DI kontajner jako service
locator (repozitář služeb).
Metodu „Resolve“ na DI kontajneru volá jen „composition root“
a infrastrukturní objekty (ControllerFactory v ukázce).
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.
28. DI kontajner není service
locator!
public MainViewModel public MainViewModel
(IWindsorContainer container) (IViewFactory viewfactory)
{ {
…. ….
} }
var childView = var childView =
container.Resolve<IChildView>; viewfactory.Create<IChildView>();
29. 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<TypedFactory
Facility>();
m_container.Register(Component.For<I
ViewFactory>().AsFactory<IViewFact
ory>());
30. Použití DI kontajneru – 3 R
Release
• 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.
31. 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
32. 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 jesusSupportService
IApocalypseRunner apocalypseRunner)
• Víte, co jsou kompozitní služby?
33. DI kontajnery v .Net světě
Castle Windsor Ninject Hiro
Autofac StructureMap Unity
?MEF? Spring.NET
34. 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…
35.
36. René Stein
• Vývoj aplikací, veřejné a inhouse kurzy
• http://www.renestein.net/nabidka.aspx
http://blog.renestein.net
http://twitter.com/renestein