Dneska si ukážeme, jak využít modularity ASP.NET MVC k tomu, abychom mohli snadno integrovat IoC kontejner. Ten pak bude sloužit k tvorbě controllerů a injekci jejich závislostí (dependecy injection). Jako kontejner použijeme Castle Windsor a konfigurační DSL v jazyce Boo – Binsor.
ASP.NET MVC je framework, který si za cíl bere jasné rozdělení odpovědností, modularitu, snadnou rozšiřitelnost a snadnou testovatelnost aplikací nad ním postavených. Což jsou nejpalčivější neduhy „klasického ASP.NET.“
Inversion of Control a Dependency Injection
Inversion of Control (IoC) je tak trochu jiný pohled na programování. Když píšete aplikaci, nepíšete ji jako program, ale jako sadu komponent, kterým „vdechnete život“ pomocí konfiguračního skriptu. Každá komponenta má v systému svoji úlohu, a pokud nám přestane její funkcionalita vyhovovat, lze ji velice snadno nahradit zásahem na jednom místě. Aby to bylo opravdu tak snadné, musí nahrazovaná komponenta splňovat určitý kontrakt – implementovat rozhranní.
Přes tato rozhranní mezi sebou jednotlivé komponenty komunikují, využívají deklarované služby, posílají si data. Jenže, jak mají vědět, kterou komponentu mají za daným rozhranním hledat. Ony to vědět nemusejí, tedy, vlastně by vůbec neměly.
Tak potom kdo? IoC kontejner!
Kontejner je mozkem aplikace. Je to ten jediný, který ví, které komponenty splňují určité kontrakty. Jak se to doví? Při startu kontejner nacpeme komponentami a on nám je později na vyžádání vrací. Dokonce jde tak daleko, že pokud si vyžádáme komponentu, vrátí nám ji včetně všech jejích závislostí a závislosti závislostí. :) Dostanete kompletní graf objektů, které jsou pro danou chvíli potřeba k vykonání dané činnosti a jsou v kontejneru zaregistrované.
Kontejner se tedy stará o dependency injection. Ale není to jediná služba, kterou nám dokáže poskytnout. Jeho další schopností je řídit životnost komponent. Už nikdy více nemusíte psát singletony! Pokud potřebujete singleton, řeknete kontejneru a on se o to postará.
Castle Windsor
Windsor je jedním z takových kontejnerů určený pro dotnet. Jistě najdete spoustu alternativních jako Spring.Net, StructureMap, Unity nebo Ninject. Každý z nich má své výhody, jiné postupy, ale i omezení. Windsor jsem si vybral z několika důvodů:
- Je součástí opensource projektu Castle,
- tudíž má celkem velkou komunitu uživatelů i vývojářů
- a navíc dokáže s dalšími projekty z Castle spolupracovat.
- Má širokou škálu způsobů konfigurace: programově, programově přes fluent interface, XML a hlavně pomocí Binsor.
Na toto téma jsem měl menší vnitrofiremní prezentaci. Slajdy z ní si může také prohlédnout.
O Binsoru je ve slajdech také zmínka a jednou už jsem o něm psal.
Integrace s ASP.NET MVC
A konečně se dostávám k tomu, o čem tento spot vlastně je. Jak integrovat Windsor kontejner do naší webové aplikace?
Předpokládám, že máte ASP.NET MVC projekt již ve svém studiu. Pak je
nutné mít binárky Windsoru. Pravděpodobně budou stačit ty z RC3.
Osobně používám aktuální verzi z trunku. Přidejte
si reference na knihovny Castle.Core,
Castle.DynamicProxy, Castle.MicroKernel a
Castle.Windsor. Pak si budeme muset vytvořit novou továrnu na
výrobu controllerů. Vlastně nemusíme, již je součástí MVC Contrib, jen jsem jí trochu
zjednodušil.
using System;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.Windsor;
namespace BlogSpots.Infrastructure {
public class WindsorControllerFactory : IControllerFactory {
private IWindsorContainer _container;
public WindsorControllerFactory(IWindsorContainer container) {
if (container == null) {
throw new ArgumentNullException("container");
}
_container = container;
}
public virtual IController CreateController(RequestContext context, string controllerName) {
controllerName = controllerName.ToLower() + "controller";
return (IController)_container.Resolve(controllerName);
}
public virtual void ReleaseController(IController controller) {
var disposable = controller as IDisposable;
if (disposable != null) {
disposable.Dispose();
}
_container.Release(controller);
}
}
}
Tato továrna bude fungovat pro Windsor. Ještě ale chci přidat podporu pro
Binsor, který je součástí Rhino.Commons.
K těm se dostanete přes SVN a spolu
s nimi dostanete i aktuální verzi Windsoru. Do projektu si ještě přidáme
reference na Rhino.Commons a Rhino.Commons.Binsor. Pak
si ještě vytvoříme statický helper pro
práci s kontejnerem.
using System;
using System.Web.Mvc;
using Castle.Windsor;
using Rhino.Commons;
namespace BlogSpots.Infrastructure {
public static class IoC {
private static readonly IWindsorContainer _container;
static IoC() {
_container = new RhinoContainer("windsor.boo");
}
public static IWindsorContainer Container {
get {
return _container;
}
}
}
}
Tím jsme si nainicializovali kontejner s konfigurací v souboru
windsor.boo. Tak, a teď už nám zbývá jen zaregistrovat
továrnu pro naši aplikaci a napsat konfigurační skript.
V Global.asax zaregistrujeme továrnu na controllery
následovně:
protected void Application_Start() {
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(IoC.Container));
}
Do web.config přidáme registraci modulu, který nám umožní
nastavit životnost objektu na jeden request:
<system.webServer>
<modules>
<add name="PerRequestLifestyle"
type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.MicroKernel" />
</modules>
</system.webServer>
Tato registrace je pouze pro IIS 7 integrated mode, je
nutné ji ještě přidat do system.web\httpModules, aby vám
fungovala i na DevServeru.
Boo na scénu
Tímto máme za sebou vše důležité, pro napsání konfiguračního skriptu a spuštění aplikace. Pokud chcete přidat do VisualStudia podporu pro Boo, ve kterém jsou Binsor konfigurační skripty psané, doporučuju stáhnout si a doinstalovat BooLang Studio.
Pojďme tedy k vytvoření konfiguračního souboru! V rootu aplikace si
vytvořte soubor Windsor.boo a nastavíme mu vlastnost Build
Action na Content, aby se nám pěkně kopíroval při
publikování projektu. Do konfiguráku zadejte následující řádky:
import System.Web.Mvc from System.Web.Mvc
for controller in AllTypesBased of Controller("<nazev assembly MVC projektu>"):
component controller.Name.ToLower(), controller:
lifestyle PerWebRequest
Takto jsme do kontejneru zaregistrovali všechny controllery v našem MVC projektu. Postupně můžeme registrovat další komponenty a budovat naší aplikaci, ale o tom zas někdy jindy. Tedy snad…
















