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

Oprava předchozích článků

11.10 - 4. ledna 2009 | Webdesign

Nikdo není neomylný, i já dělam chyby. A poměrně často. Bohužel i ve svých článcích. Proto jsem se jal revidovat sérii předchozích článků o ASP.NET MVC a znovu si prošel jejich kód a ověřil, že funguje.

Prošel jsem tedy následující články:

Doplnění REST

V článku REST aplikace pomocí ASP.NET MVC jsem nastínil, jak vytvořit RESTful přístup k entitám pomocí filtrů akcí. Výslednou ukázku jsem doplnil o výstupy akce. V komentářích padlo několik dotazů, jak vlastně REST služby volat, protože zatím žádný prohlížeč neumí formulář odeslat jinou metodou než POST nebo GET.

Jediným řešením, jak ze stránek REST používat je AJAX. Proto akce vrací JSON. V pohledech pak postačí následující kód:

<% using (Ajax.BeginForm("REST", "Customers", new AjaxOptions { HttpMethod = "POST" })) {%>
  <input type="submit" value="Create New" />
<% } %>

<% using (Ajax.BeginForm("REST", "Customers", new AjaxOptions { HttpMethod = "PUT" })) {%>
  <input type="submit" value="Update" />
  <input type="hidden" name="id" value="10" />
<% } %>

<% using (Ajax.BeginForm("REST", "Customers", new AjaxOptions { HttpMethod = "DELETE" })) {%>
  <input type="submit" value="Delete" />
  <input type="hidden" name="id" value="10" />
<% } %>

<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>

Vylepšená verze model binderu

V článku Model binders v ASP.NET MVC jsem ukazoval jak si napsat vlastní model binder. Taky jsem psal že uvedený postup není moc dobrý. Tady je tedy lepší verze:

public class AddressBinder : BinderBase<Address> {
  public override ModelBinderResult BindModel(ModelBindingContext bindingContext) {

    IValueProvider valueProvider = bindingContext.ValueProvider;
    Address address = bindingContext.Model as Address ?? new Address();

    address.Street = GetSafeStringValue(valueProvider, "address_street");
    address.StreetNumber = GetSafeStringValue(valueProvider, "address_street_number");
    address.PostalCode = GetSafeStringValue(valueProvider, "address_postal_code");
    address.Town = GetSafeStringValue(valueProvider, "address_town");

    return new ModelBinderResult(address);
  }
}

K tomu je ale ještě potřeba rozšířit bázovou třídu binderu:

public abstract class BinderBase<T> : IModelBinder {

  protected static string GetSafeStringValue(IValueProvider valueProvider, string key) {
    ValueProviderResult value = valueProvider.GetValue(key);

    if (value != null) {
      return value.AttemptedValue.Trim();
    }

    return String.Empty;
  }

  protected static bool GetSafeBooleanValue(IValueProvider valueProvider, string key) {
    ValueProviderResult value = valueProvider.GetValue(key);

    return value != null;
  }

  public Type ModelType {
    get {
      return typeof(T);
    }
  }
  #region IModelBinder Members

  public abstract ModelBinderResult BindModel(ModelBindingContext bindingContext);
  #endregion
}

Tím jsme se zbavili závislosti na HttpContextu, binder je lépe testovatelný a hlavně se dá použít i v metodě UpdateModel.

Oprava integrace IoC kontejneru do ASP.NET MVC

V ukázkových kódech článku Inversion of Control v ASP.NET MVC bylo několik chybek, chyběly usingy a některé věci jsem trošku refaktoroval.

Nová verze RoutingMacros.boo

V článku DSL pro konfiguraci URL Routingu v ASP.NET MVC jsem měl jedno makro nedopsané. V nové verzi jsem přidal sekci constraints k makru ignore_route a provedl lehký refaktoring. Plně funkční kód:

import Boo.Lang.Extensions
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Ast.Visitors

def apply_constraints(block as Block, constraints as MacroStatement):
  assert block
  return if not constraints

  block.Add([| route.Constraints = RouteValueDictionary() |])

  for exp as ExpressionStatement in constraints.Block.Statements:
    binary = exp.Expression as BinaryExpression
    block.Add([| route.Constraints.Add($(binary.Left.ToString()), $(binary.Right)) |])

def apply_defaults(block as Block, defaults as MacroStatement):
  assert block
  return if not defaults

  block.Add([| route.Defaults = RouteValueDictionary() |])

  for exp as ExpressionStatement in defaults.Block.Statements:
    binary = exp.Expression as BinaryExpression
    block.Add([| route.Defaults.Add($(binary.Left.ToString()), $(binary.Right)) |])

macro ignore_route:
  path, = ignore_route.Arguments
  constraints = ignore_route["constraints"] as MacroStatement

  block = Block()
  block.Add([| route = Route($path, StopRoutingHandler()) |])
  apply_constraints(block, constraints)
  block.Add([| RouteTable.Routes.Add(route) |])

  return block

macro map_route:
  name, path = map_route.Arguments
  defaults = map_route["defaults"] as MacroStatement
  constraints = map_route["constraints"] as MacroStatement

  block = Block()
  block.Add([| route = Route($path, MvcRouteHandler()) |])
  apply_defaults(block, defaults)
  apply_constraints(block, constraints)
  block.Add([| RouteTable.Routes.Add($name, route) |])

  return block

macro defaults:
  allowedParents as List = [ "map_route" ]
  parent as MacroStatement = defaults.GetAncestor(NodeType.MacroStatement)
  assert allowedParents.Contains(parent.Name)

  parent["defaults"] = defaults

macro constraints:
  allowedParents as List = [ "map_route", "ignore_route" ]
  parent as MacroStatement = constraints.GetAncestor(NodeType.MacroStatement)
  assert allowedParents.Contains(parent.Name)

  parent["constraints"] = constraints

Autor: Aleš Roubíček | Zatím bez komentáře | Delicious | FriendFeed | Facebook | Linkuj!

Pojmenované routy v ASP.NET MVC

21.10 - 3. ledna 2009 | Webdesign

Minule jsme si ukázali, jak jednodušeji definovat routy v ASP.NET MVC pomocí maker v jazyce Boo. Prvním parametrem pravidla pro vytvoření routy je její název. K čemu je užitečný?

Když začnete psát větší aplikaci v ASP.NET MVC, zjistíte, že si s výchozí routou {controller}/{action}/{id} dlouho nevystačíte. Ať už je to tím, že chcete mít aplikaci lokalizovatelnou do více jazyků včetně URL adres, nebo tím, že prostě název řadiče (controlleru) není pro URL zrovna vhodný. Je k tomu spousta důvodů, ale ten nejzákladnější bude, že ne všechny akce mají pouze jeden parametr a ještě k tomu id…

Definice routovací tabulky

K definici fiktivní routovací tabulky využijeme DSL vytvořenou v minulém spotu. Každý správný vývojář určitě nepoužívá čísla, ale ani řetězce! Proto si nejprve vytvoříme statickou třídu pro uskladnění názvů našich rout.

namespace MyWeb.Routing {
  public static class Routes {
    public const string EditArticle = "edit article";
    public const string ListArticles = "list articles";
  }
}

A nyní přejdeme k definování routovací tabulky.

import file from RoutingMacros.boo

import System.Web.Routing
import System.Web.Mvc

import MyWeb.Routing from MyWeb

articles_controller = "Articles"

map_route Routes.EditArticle, "admin$/articles/editor/{id}":
  defaults:
    controller = articles_controller
    action = "Edit"

map_route Routes.ListArticles, "admin$/articles":
  defaults:
    controller = articles_controller
    action = "Index"

Řetězce šablony routy jsou rovněž dobrými adepty na přesun do resource souboru, ale pro tentokrát se spokojíme s jejich přítomností. Pro dnešní ukázku to plně dostačuje. :)

Tak, routovací tabulku máme nadefinovanou a k čemu nám to vlastně je?

Použití v praxi

Když jsem onehdá psal o tom, jak funguje URL Routing v ASP.NET MVC, tak jsem nakousnul téma obousměrného routingu. Na WebExpu na to už tolik času taky nebylo, tak nezbývá, než se k tomu vrátit nyní. Pro někoho to bude možná překvapení, ale routovací tabulky lze využít i k vytváření odkazů! A taky se toho hojně využívá. Určitě každý, kdo už si ASP.NET MVC zkoušel, potkal HtmlHelper a jeho extenzi ActionLink. Užití v šabloně pohledu je asi následující:

<%= Html.ActionLink("Vypiš články", "Index") %>

Tento helper se podívá do routovací tabulky, z kontextu si vezme název aktuálního řadiče (vpodstatě všechny vlastnosti aktuální routy) a spojí ho s názvem akce, který byl definován jako parametr helperu (v tomto případě Index). Tyhle dvě (popř. více) hodnoty hledá v tabulce, najde šablonu routy ("admin$/articles"), nacpe do ní získané hodnoty (v tomto případě žádné) a vrátí to celé jako odkaz:

<a href="admin$/articles">Vypiš články</a>

Výhodou užití takového helperu je, že pokud změníte routu, tak se vám automaticky generují správné odkazy. Odkazy na akci jsou dobré v kontextu jednoho řadiče. Jenže, když máme trochu víc řadičů a navíc na jejich akce odkazujeme z pohledů jiných řadičů, musíme přidávat další parametr, který definuje, o jaký řadič se jedná, na jehož akci chceme odkázat. Věci by měli zůstávat co nejjednodušší. Proto je zde další helper, který dělá odkazy na pojmenované routy.

<%= Html.RouteLink("Vypiš články", Routes.ListArticles) %>

Výsledek je totožný s předchozím, ale jsou tu i jisté jiné benefity. Název routy je totiž klíčem v routovací tabulce, proto je vytváření odkazu na pojmenovanou routu rychlejší/výkon­nější (nemusí se vyhledávat v hodnotách, ale je tu přístup přímo přes index). Proto se je nebojte používat, kde to jen jde…

Závěr

Obousměrný routing je v ASP.NET MVC velice silná věc a prozkoumat jeho temná zákoutí a pochopit, co se k čemu hodí, může zabrat nějaký ten čas. Doufám, že jsem vám trošku to prozkoumávání usnadnil. Ale nebojte, zanedlouho by měla vyjít RC verze ASP.NET MVC, kde přibudou další zajímavé helpery.

Autor: Aleš Roubíček | 2x komentováno | Delicious | FriendFeed | Facebook | Linkuj!

Agregace a sdílení

15.08 - 2. ledna 2009 | Moje práce

Vrámci zdokonalování tohoto blogu jsem do nového roku přidal několik usnadnění jak sdílet a agregovat moje články.

Agregace

Box s možností přidat si tyhle stránky do své oblíbené čtečky najdete na konci stránky. Nejprve jsem přidal (vrátil) pár odkazů na přidání RSSka do Googlu, Windows Live a na Seznam.cz, což jsou nejpoužívanější portály mých návštěvníků. Pak jsem přidal možnost zaregistravat si můj FriendFeed, kde můžete sledovat nejen moje blogové příspěvky, ale i štěbetání a další aktivity v rámci sociální sítě. A když už jsem byl v tom, tak jsem ještě prolezl logy a podíval se z jakých agregátorů sem ještě lidi chodí a vyšlo mi z toho, že ještě musím přidat Bloglines, Netvibes a Newsgator, abych svým čtenářům registraci do jejich čtečky usnadnil. Tak jsou tam…

Sdílení

U každého článku jsem měl v zápatí odkaz na sdílení v Delicious a v českém Linkuj! a když už jsem byl u toho FriendFeedu tak jsem přidal možnost na něm sdílet i samotné články. Jenže hodně lidí používá hlavně Facebook, tak proč nejít „stádu“ naproti? :) Pak je tu ještě Digg a českej Jagg. Tak doufám, že teď budete mít, drazí čtenáři, snažší sdílet mé bombastické články se svými přáteli.

Jaké služby používáte k agregaci a sdílení vy?

Autor: Aleš Roubíček | 1x komentováno | Delicious | FriendFeed | Facebook | Linkuj!

DSL pro konfiguraci URL Routingu v ASP.NET MVC

15.11 - 27. prosince 2008 | Webdesign

Binsor je skvělou konfigurační DSL pro Windsor IoC kontejner. Proč nevyužít možností, které nám využití jazyka Boo přináší a nerozšířit si Binsor o pár dalších pravidel pro konfiguraci routingu v ASP.NET MVC?

Routing v ASP.NET MVC

System.Web.Routing je jedním ze základních kamenů ASP.NET MVC. Přijímá parametry z URL a posílá je MvcRouteHandleru, který vybere controller a jeho akci, která má požadavek zpracovat. Routing je na MVC nezávislý a využívá se třeba i v ASP.NET Dynamic Data.

Pokud chceme routing nakonfigurovat, máme několik možností, jak toho dosáhnout. Tou první je využít standardní API:

public void Application_Start() {
  RouteTable.Routes.Add(
    new Route("{resource}.axd/{*pathInfo}", new StopRoutingHandler())
  );

  RouteTable.Routes.Add("default",
    new Route("{controller}/{action}/{id}", new MvcRouteHandler()) {
      Defaults = new RouteValueDictionary(new {
        controller = "Home",
        action = "Index"
      })
    }
  );
}

Jak jistě vidíte, psát takto routovací pravidla je celkem na dlouho. Kód obsahuje zbytečně mnoho šumu a můžete snadno něco opomenout. Proto je součástí MVC frameworku několik extenzí, které mají konfiguraci usnadnit:

public void Application_Start() {
  RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

  RouteTable.Routes.MapRoute(
    "default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index" }
  );
}

Takový kód už je o poznání kratší, většina šumu už je pryč, ale stejně tu zůstává několik zbytečných znaků a na první pohled není vidět, co vlastně je ta anonymní třída. Navíc jde o konfiguraci a ta by si zasloužila být v konfiguračním souboru. Změna routy si pak nevyžádá rekompilaci aplikace, ale pouze restart… Proto jsem se rozhodl vytvořit si vlastní konfigurační DSL a to za pomocí Boo.

Krok první: návrh DSL

Začněme tedy s tím, jak by taková DSL měla vypadat. To, co má dělat, je už nastíněno výše. První můj návrh vypadal asi takto:

ignore:
  route "{resource}.axd/{*pathInfo}"

map:
  route "Default", "{controller}/{action}/{id}":
    defaults:
      controller = "Home"
      action = "Index"

Bloky ignore a map by mohli mít vícero potomků route. Tuto variantu jsem však záhy zapověděl, protože vyžaduje zbytečné odsazení navíc a v delších konfiguracích už nemusí být jasné, jestli zrovna jsme v bloku ignore nebo map popřípadě jiném. Tak jsem nad tím přemýšlel a nakonec zvolil následující variantu:

ignore_route "{resource}.axd/{*pathInfo}"

map_route "Default", "{controller}/{action}/{id}":
  defaults:
    controller = "Home"
    action = "Index"

Takže syntaxi bych měl, teď přijde ta veselejší část: implementace.

Krok druhý: implementace DSL

Jedním z důvodů proč jsou oblíbené dynamické jazyky jako Ruby a Python je meta programování. Meta programování nám umožňuje vytvářet nové programové konstrukce v jazyku, zkrátka obohatit jeho sémantiku. Já si pro implementaci vybral Boo.

Ačkoli je Boo statický jazyk, díky otevřenému konceptu kompilátoru, můžeme jazyk obohacovat stejně efektivně, jako v jazyce dynamickém. Výhodou je, že gramatickou správnost našich rozšíření můžeme ověřit při kompilaci. Základním kamenem při tvorbě vlastních DSL jsou meta funkce, které jsem však nepoužil, a makra.

Makro je třída. Ano lze ho napsat v jakémkoli jazyce nad CLI. Stačí nám implementovat rozhraní IAstMacro a třídu pojmenovat s konvenčním přídomkem Macro, např. Map_routeMacro. V implementovaných metodách si pak můžete dle libosti hrát s AST. Takhle je v C# napsanej třeba Binsor. Já jsem však k implementaci routovacích maker zvolil přímo jazyk Boo, který má od verze 0.8.1 podporu pro snadné psaní maker přímo v jazyce.

Začneme jednodušším makrem pro ignorování routy:

import Boo.Lang.Extensions
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast
import Boo.Lang.Compiler.Ast.Visitors

macro ignore_route:
  path, = ignore_route.Arguments
  block = Block()
  block.Add([| RouteTable.Routes.Add(Route($path, StopRoutingHandler())) |])
  return block

Pro snadnější pochopení si kód trochu popíšeme. Na prvních řádkách si naimportujeme potřebné jmenné prostory. Klíčovým slovem macro zahájíme psaní makra a v zápětí mu přiřadíme jméno, protože, co dokážeme pojmenovat, dokážeme ovládat. Pak si načteme první parametr a uložíme si ho do proměnné path. Pak si vytvoříme nový blok kódu. A teď začíná magic. Všechno co je v bloku [| … |] je normální kód v Boo, který se převede na AST. Pomocí $ lze do toho kódu zasahovat zvenčí, takže místo $path bude v kódu obsah proměnné path. Připravený blok kódu pak vrátíme kompilátoru.

Díky našemu makru se kód ignore_route "test" přeloží na RouteTable.Routes.Add(Route("test", StopRoutingHandler())) a pokud je naimportován jmenný prostor System.Web.Routing, kompilátor všemu rozumí a máme jednoduchou syntaxi pro definování ignorovaných rout.

Bylo to celkem snadné, že? Satačí pochopit základní princip. Ono totiž klíčové slovo macro není nic jiného než makro, které bere jako první parametr název makra a následující blok je de facto implementací metody Expand. :)

Postoupíme dál. Makro map_route už bude o něco větší oříšek, protože může obsahovat vnořené bloky defaults, kde jsou vypsané výchozí hodnoty parametrů routy, a constraints, kde jsou podmínky, které tyto parametry musejí splňovat.

macro map_route:
  name, path = map_route.Arguments
  defaults = map_route["defaults"] as MacroStatement or MacroStatement()
  constraints = map_route["constraints"] as MacroStatement or MacroStatement()

  block = Block()
  block.Add([| route = Route($path, MvcRouteHandler()) |])
  block.Add([| route.Defaults = RouteValueDictionary() |])
  block.Add([| route.Constraints = RouteValueDictionary() |])

  for exp as ExpressionStatement in defaults.Block.Statements:
    binary = exp.Expression as BinaryExpression
    block.Add([| route.Defaults.Add($(binary.Left.ToString()), $(binary.Right)) |])

  for exp as ExpressionStatement in constraints.Block.Statements:
    binary = exp.Expression as BinaryExpression
    block.Add([| route.Constraints.Add($(binary.Left.ToString()), $(binary.Right)) |])

  block.Add([| RouteTable.Routes.Add($name, route) |])

  return block

macro defaults:
  allowedParents as List = [ "map_route" ]
  parent as MacroStatement = defaults.GetAncestor(NodeType.MacroStatement)
  assert allowedParents.Contains(parent.Name)

  parent["defaults"] = defaults

macro constraints:
  allowedParents as List = [ "map_route", "ignore_route" ]
  parent as MacroStatement = constraints.GetAncestor(NodeType.MacroStatement)
  assert allowedParents.Contains(parent.Name)

  parent["constraints"] = constraints

Makro map_route je trošku rozšířenou obdobou ignore_route, podívejme se na makro defaults, které přináší něco jiného. Zprvu si nadefinujeme, jaká makra mohou toto makro obsahovat, a následně ověříme, že opravdu není v jiném makru. Pak vezmeme kód tohoto makra a pošleme ho ke zpracování nadřazenému makru. Žádný kód generovat nebudeme. Makro constraints je na tom téměr stejně, jen předpokládá, že bude i v ignore_route, což zatím není implementováno, ale budiž.

Vraťme se k makru map_route a konkrétně k for cyklům. Tam totiž projíždíme předané kusy kódu. Jednotlivé řádky považujeme za výrazy. Konkrétně výraz přiřazení, což je binární výraz. Binární výraz má vždy pravou a levou stranu a operátor. Na levé straně máme klíče do RouteValueDictionary, což jsou řetězce. Proto levou stranu musíme převést na řetězec metodou ToString(). Pravá strana je hodnota přidružená ke klíči a může to být cokoli (object). Takže tam i cokoli pošleme.

Vezmeme oba kusy kódu a uložíme je do souboru RoutingMacros.boo

Krok třetí: užívání DSL

Než začneme makra využívat, musíme si je nejprve naimportovat. Na začátek souboru Windsor.boo přidáme řádky:

import file from RoutingMacros.boo
import System.Web.Routing
import System.Web.Mvc

No a potom už můžeme na konci souboru přidávat routovací pravidla.

Autor: Aleš Roubíček | 4x komentováno | Delicious | FriendFeed | Facebook | Linkuj!

Užívejte Vánoce

13.52 - 24. prosince 2008 | Jen tak

Užívejte Vánoce dle svých představ, třeba v kruhu svých blízkých, na kterých vám záleží, nebo o samotě. :)

Autor: Aleš Roubíček | Zatím bez komentáře | Delicious | FriendFeed | Facebook | Linkuj!

Model binders v ASP.NET MVC

20.15 - 13. prosince 2008 | ASP.NET 2.0

Jednou z pěkných vlastností ASP.NET MVC je, že data která posíláte akci, můžete v kódu získávat přes silně typové parametry metody (akce). Pokud dostatečně dodržujete dané konvence, můžete takto získat i třeba komplexní typy.

To je zařízeno něčím, co se jmenuje model binders. Základem je DefaultModelBinder, který implementuje rozhranní IModelBinder. Dále pak atributy BindAttribute a jeho bratříček k psaní vlastních atributů CustomModelBinderAttribute a v neposlední řadě metody řadiče UpdateModel a TryUpdateModel.

O tom jak používat DefaultModelBinder, si můžete přečíst třeba u ScottaGu. Já bych se spíš chtěl zaměřit na možnosti rozšíření, které nám infrastruktura model binders přináší.

Velkou slabinou konvenčního DefaultModelBinderu je, že ve view musíte dodržovat jmenné konvence vašeho modelu. Sice to může přinést spoustu benefitů, ale i problémů.

Možnosti

Možností, jak bindery používat, je mnoho. Můžete si třeba bindovat z AppSettings nebo z třeba z cookie. I to je pomocí model binders možné. Pojďme se ale podívat na to, jak bindovat váš model na data odeslaná z view s odlišnou jmennou konvencí…

Základy

Vlastní bindery můžeme vytvořit tak, že implementuje rozhraní IModelBinder. Hotovou implementaci pak musíme zaregistrovat při startu aplikace. Pro ukázku si uděláme velice jednoduchý příklad načítání adresy z FormsCollection. Mějme datovou třídu adresa:

public class Address {
    public string Street { get; set; }
    public string StreetNumber { get; set; }
    public string Town { get; set; }
    public string PostalCode { get; set; }
}

K ní si vytvoříme model binder:

public class AddressBinder : IModelBinder {
  public ModelBinderResult BindModel(ModelBindingContext bindingContext) {
    var address = new Address {
      Street = bindingContext.HttpContext.Request["address_street"].Trim(),
      StreetNumber = bindingContext.HttpContext.Request["address_street_number"].Trim(),
      PostalCode = bindingContext.HttpContext.Request["address_postal_code"].Trim(),
      Town = bindingContext.HttpContext.Request["address_town"].Trim(),
    };

    return new ModelBinderResult(address);
  }
}

Který nakonec zaregistrujeme v Global.asax:

protected void Application_Start() {
  ModelBinders.Binders[typeof(Address)] = new AddressBinder();
}

Když teď akci předáme parametr typu Address, bude automaticky vázán pomocí AddressBinderu. Stejně tak, použijeme-li metodu UpdateModel. V některých scénářích zjistíte, že výše uvedený binder, není úplně skvělý, ba co víc, že je v podstatě dost k ničemu. Navíc taky přijdete na to, že když váš model bude trochu bohatší, tak se váš Global.asax pěkně natáhne, navíc pokud budou mít vaše bindery závislosti na jiných službách, začne v tom být pěkný bordel…

Binsor na scénu

Minule jsme si ukázali, jak propojit ASP.NET MVC s IoC kontejnerem a dnes ho využijeme a trochu si ulehčíme práci… Jak jsem již psal, všechny model bindery implementují rozhranní IModelBinder a toho můžeme využít pro jejich registraci do kontejneru, přidáním následujících řádků do souboru Windsor.boo:

for binder in AllTypesBased of IModelBinder("<nazev assembly s Model Bindery>"):
  component binder.Name.ToLower(), IModelBinder, binder

Máme je zaregistrované v kontejneru, ale potřeby psát něco do Global.asax jsme se nezbavili. To je pravda, ale vzápětí to napravíme. Budeme ještě potřebovat generickou bázovou třídu. Proč gerenerickou? No, je v tom takovej fígl – ten prozradím až za chvíli. :) Teď k věci:

public abstract class ModelBinderBase<T> : IModelBinder {
  public Type ModelType {
    get { return typeof(T); }
  }
  public abstract ModelBinderResult BindModel(ModelBindingContext bindingContext);
}

V podstatě jsme rozšířili rozhraní IModelBinder o znalost typu datového objektu se kterým pracuje. Proč? Vzpomeňte si na registraci binderu, kde je klíčem ve slovníku binderů typ datového objektu. Ano, to je on!

Jěště trochu poupravíme náš AddressBinder:

public class AddressBinder : ModelBinderBase<Address> {
  public override ModelBinderResult BindModel(ModelBindingContext bindingContext) {
    var address = new Address {
      Street = bindingContext.HttpContext.Request["address_street"].Trim(),
      StreetNumber = bindingContext.HttpContext.Request["address_street_number"].Trim(),
      PostalCode = bindingContext.HttpContext.Request["address_postal_code"].Trim(),
      Town = bindingContext.HttpContext.Request["address_town"].Trim(),
    };

    return new ModelBinderResult(address);
  }
}

Pořád tu zůstává ta nepěkná závislost na HttpRequestu, je snadno řešitelná, ale našemu příkladu nevadí a vypořádáme se s ní někdy jindy… Takže vraťme se zase k Windsor.boo a na jeho konec přidejme následující řádky:

for modelBinder as duck in IoC.Container.ResolveAll of IModelBinder():
  ModelBinders.Binders[modelBinder.ModelType] = modelBinder

Upozorňuji, že tyto řádky musí být až na konci souboru. Musí se volat, až po tom, co se zaregistrují všechny komponenty, protože tady si vyzvedáváme již hotové bindery z kontejneru a registrujeme je do ASP.NET MVC.

Možná jste si povšimli formulky as duck. Boo je staticky typovaný jazyk, stejně jako C#, jen využívá implicitního typování. V tomto případě nám generická metoda ResolveAll vrací hotové instance, které implementují rozhranní IModelBinder a taky mají tento silný typ. A toto rozhraní neví nic o tom s jakým typem modelu je svázáno.

Naštěstí Boo podporuje duck typing, což nám přidává tak trochu dynamičnost – pozdní vazbu. Já vím, že všechny moje bindery dědí z bázové třídy, která má vlastnost ModelType a s použítím as duck jí můžu zavolat. Tohle je vlastnost, kterou bude C# umět až ve verzi 4.0, do té doby je v něm toto velice těžko řešitelné (osobně jsem se o to ani nepokoušel, ale nejspíš nějak přes reflexi by to jít mělo).

Tím jsme se zbavili nutnosti registrovat každý model binder zvlášť.

Autor: Aleš Roubíček | 2x komentováno | Delicious | FriendFeed | Facebook | Linkuj!

Lehký facelift

17.04 - 7. prosince 2008 | Moje práce

Dnes jsem zapracoval něktreté drobné změny designu, které jsem už delší dobu připravoval. Hlavním cílem změny byla patička webu, protože na ní jsem se při původním návrhu moc nesoustředil.

Nyní by měla být trochu uspořádanější. Styl jsem tak trochu opajcnul ze slajdů PDC 2008. Homepage taky doznala lehké přeurovnání do mřížky. Další změnou jsou fonty. V boxu s agregací blogu, by měl být styl více podobný tomu, co je na blogu. Trochu se změnil i hledací formulář, který teď kopíruje aktuální styl Live Search, který je pro zdejší hledání použit.

Změny to jsou jen kosmetické, ale i tak zabraly dost času. Každopádně to není ještě vše. Na blogu brzo přibude patičkový box pro agregaci. Další EVO by pak měla mít jeden stylopis pro celý web. Že by dárek k pátým narozeninám?

Autor: Aleš Roubíček | 2x komentováno | Delicious | FriendFeed | Facebook | Linkuj!

Odpojte se k nám!

08.29 - 2. prosince 2008 | Webdesign

Sháníme posily do našeho týmu. Pokud znáš dobře dotnet nebo Javu a chceš se učit novým věcem, tak čti dál.

Představení

Jsme Twareg (čteme tuareg, ale někdy i tvarek), firma zabývající se vývojem webových aplikací, které jsou výkonné, dostupné a škálovatelné. K tomu primárně užíváme platformu Microsoft .NET (C#, ASP.NET). Používáme nejmodernější technologie (např. ASP.NET MVC, Dynamic Data, WCF, „Velocity“) a snažíme se o moderní postupy při vývoji našeho software. Všichni máme zkušenosti z portálu Atlas.cz a jsme ochotní se o ně podělit. Když řeknu, že jsme dynamický tým, asi bych lhal. Představte si dynamický tým a svou představu vynásobte deseti. :)

Naše kancelář je vybavena různobarevnými pěnovými míčky, a když vás někdo naštve, můžete si po něm hodit. Dostáváme stravenky a chodíme je společně projíst někam na Anděla. Věhlasný je také náš rituál vaření, který jsme si přinesli z Atlasu.

Pokud hledáte naše webové stránky, tak vězte, že jsme si žádné ještě nestihli udělat, ale máme to v plánu. Pokud vás zajímá naše zázemí, tak vězte, že jsme sesterskou společností Ataxa a sídlíme ve stejné budově.

Na čem děláme

Našim hlavním produktem je codename „Hyas“. Je to reklamní systém, který si klade za cíl poučit se z chyb konkurenčních systémů, zachovat si jejich výhody a přinést něco nového. Být prostě lepší. Jak prosté. Nyní máme téměř připravené jádro systému, které půjde brzo do testování. Potřebujeme ale další lidi, kteří by nám pomohli nad ním napsat veřejné API (SOAP, REST), administrační/u­živatelské rozhranní a další potřebné komponenty. Pokud vás toto zajímá, neváhejte mě kontaktovat!

Pokud byste raději dělali na webech, je tady ta možnost také. Děláme na Web 2.0 projektech založených na znovupoužitelných komponentách (a tím rozhodně nemyslím jen server controly). Aktuálně děláme katalog nightlife podniků se silnou integrací sociálních prvků. A ve frontě čekaj další neméně zajímavé. Pokud se na nich chcete podílet, ozvěte se mi!

Koho hledáme

Předně schopné vývojáře. Takové, co mají chuť se učit novým věcem a chtějí se o své poznatky dělit. :) Takové, co mají týmového ducha, kterým nevadí zůstat občas v práci dýl a zahrát si třeba Wiičko nebo jít společně do kina, na bowling nebo jenom na jedno.

Autor: Aleš Roubíček | 12x komentováno | Delicious | FriendFeed | Facebook | Linkuj!

Trenčanské párky s fazolí po mexicku

15.01 - 29. listopadu 2008 | Jen tak

Vaření konzerv Trenčanské párky s fazolí patřilo ke koloritu mých studentských let. Sice už jsou dávno pryč, ale proč některé vzpomínky neoživit? :)

Prefabrikované potraviny mají tu výhodu, že jsou velice rychle hotové a většinou jsou i stravitelné. Když už jste donuceni něco takového jíst, proč si to trochu nezpestřit? Dnes přináším krátký návod na lehký upgrade.

Nakrájíme anglickou slaninu (nebo uzený vepřový bok) na větší kousky a osmahneme na pánvičce. Přidáme nahrubo nasekanou cibulku a necháme zesklovatět. Do připraveného základu vmícháme obsah konzervy. Necháme ohřát na mírném plameni, aby se fazolky nepřipekly. Ostrost dodáme špetkou Jolokii (pozor hodně opatrně), nebo kajenským pepřem. Řádně rozmícháme. Na závěr vmícháme na kostičky nakrájený balkánský sýr a necháme lehce zatavit. Servírujeme do hlubokého talíře s čerstvým pečivem. Jídlo je možno dozdobit na kolečka nakrájenou červenou cibulí a čerstvou petrželkou.

Dobrou chuť.

Autor: Aleš Roubíček | Zatím bez komentáře | Delicious | FriendFeed | Facebook | Linkuj!

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…

Autor: Aleš Roubíček | Zatím bez komentáře | Delicious | FriendFeed | Facebook | Linkuj!