Klávesové zkratky na tomto webu - rozšířené Na obsah stránky

Inversion of Control v ASP.NET MVC

10.30 - 24. listopadu 2008 | ASP.NET 2.0

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ů:

  1. Je součástí opensource projektu Castle,
  2. tudíž má celkem velkou komunitu uživatelů i vývojářů
  3. a navíc dokáže s dalšími projekty z Castle spolupracovat.
  4. 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…

Komentáře RSS

  1.  

    .jarin

    12.05 - 6. března 2009 | #

    Ahoj,
    trochu z jineho soudku. Vybiram persistentni/ aplikacni framework, takze si delam mensi analyzu nad nekolika resenimi. Docela se mi zamlouva Castle Project + NHibernate. Bohuzel nemam moc casu rozjizdet vsechny frameworky a detailne je testovat. Dale mi jde o to co nejmene psat dokola se opakujici/(v dobe vyvoje stale meneny) kod, takze bych rad vsechno generoval. V tuto chvili pouzivam pro generovani entit z DB CodeSmith. Co se tyka Castle + NHibernate, tak jsem totalne ztracen v nekolika opravdu zakladnich vecech:

    1. V pripade Castle se hodne mluvi o Active record nad NHibernate – to znamena, ze ja si vygeneruju entity v NHibernate a nad temito NHibernate entitami budu muset vytvorit jeste jednu vrstvu AR Castle?
    2. Musim pro kazdou entitu NHibernate neco v castle konfigurovat?
    3. ma vubec Castle nejaky generator, kde bych stiskl button, napsal connection string a nastavil par atributu a ono by mi to vytvorilo kostru 3-vrstve aplikace a vyplnilo konfiguraky, jako je tomu napriklad u CodeSmith – sablona NetTiers ?

    Diky moc za pripadnou odpoved. V tom propojeni NHibernate + Castle mam mezeru. Ale, kdyz jsem prochazel stranky Castle projectu ale i springu, tak mi docela chybel zretelne napsany text, nebo obrazek, ktery by tohle uplne nove prichozimu priblizil.

  2.  

    Aleš Roubíček

    15.15 - 6. března 2009 | #

    Castle ActiveRecord zjednodušuje práci s NHibernate. V NHibernate musíš psát XML konfiguraci pro každou entitu, v ActiveRecord toho smého efektu dosáhneš odekorováním třídy a vlastností atributama. Pak je potřeba do konfiguráku přidat pár klíčů pro nastavení NHibernate (dialect, connection string atd.) a při startu aplikace tuto konfiguraci načíst. Nebo použít integraci do Windosoru/Binsoru.

    Pro generování existuje nástroj, který se jmenuje ActiveWriter. Ale nejsem zastáncem generovaného kódu, mám rád věci plně pod kontrolou. :) Proto je mi třeba Castle ActiveRecord bližší neš LINQ to SQL nebo Entity Framework.

  3.  

    .jarin

    16.00 - 6. března 2009 | #

    [2] Aleš Roubíček: Zajimave, diky za odpoved. Moc mi to pomuze.

    Kdybych nepouzil Castle a mel bych jenom NHibernate, potom bych pracoval s repository patternem? Zatimco v Castlu bych si vytvarel Active recordy (ktere by nasledne volali NHibernate)?

    Co dale v praxi pouzivas z Castlu ?

    Ten Active writer vypada dobre.

    Z psani konfiguraku v NHibernate se mi dela husi kuze. V CodeSmithu lze provadet reverse mapping bez toho, aniz bych musel nejake konfiguraky psat. viz tento webcast: http://www.co­desmithtools.com/…ber­nate.html

    Nevis, jestli bych takto vygenerovane tridy mohl pouzivat v Castlu ?

    Zatim diky za rychlou odpoved, konecne v tom zacinam mit poradek.

    Mej se

  4.  

    Aleš Roubíček

    17.00 - 6. března 2009 | #

    Projdi si tenhle blog píšu tu, jak použít Castle ActiveRecord s Repository Patternem. Já ActiveRecord používám jen na mapování ne jako ActiveRecord pattern :) V praxi z Castle ještě používám Windsor/Binsor. Jak říkám projdi si blog – úplně dole je tagcloud kde najdeš co potřebuješ :)

Místo pro tvůj názor

Povinné je jméno a komentář, z e-mailu se rozpoznají Gravatary.
Komentář je formátován pomocí Texy! syntaxu.
Například: **tučný text**, *kurzíva*, "text odkazu":adresa.
Internetové adresy jsou převáděny na odkazy.
Na komentáře se můžete odkazovat pomocí [číslo komentáře].

Nový komentář