Non sono propriamente uno sviluppatore web, o almeno non lo sono più in modo assiduo da qualche anno, quindi sono rimasto un po’ indietro :-)
Da un anno, abbondante, a questa parte in bottega stiamo seguento un cliente, di dimensioni ragguardevoli, nello sviluppo di una soluzione di content publishing; sono entrato nel team da poco e siccome una parte corposa del progetto è basta su Asp.Net MVC avevo bisogno di recuperare il tempo perso e nel frattempo prendere confidenza con il framework MVC, naturalmente non potevo farlo pesando sulle spalle del team, almeno non per la parte prettamente infrastrutturale.
Mi sono armato di buona volontà e ho iniziato la creazione di un “piccolo” progetto che avevo in cantiere da un po’, progetto che poteva essere fatto con MVC, progetto che ha caratteristiche che lo rendono compatibile con le mie necessità dilearning e altre caratteristiche che possono essere piegate alle mie necessità di learning :-)
Naturalmente io sono allergico al manuale delle istruzioni e peggio ancora ho la tendenza a voler partire dalla cose facili… :-), ergo:
e mi sono detto:
  • devo riuscire a pluggare Castle Windsor come framework per IoC;
  • devo riuscire a spostare i Controller(s) in un assembly separato;
e con sorpresa ho scoperto che è di una semplicità disarmante, il che, imho, è decisamente un pregio.
ControllerFactory
L’architettura di MVC prevede che la creazione di un controller sia delegata ad una factory, ergo quando registrate le route:
routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = "" }
);
oltre a fare tutto quello che fanno state anche dicendo: “ooh engine di MVC! guarda che per soddisfare a quella richiesta devi usare il controller che si chiama HomeController e che sta nella folder Controllers”, conventtion-over-configuration.
In realtà non è vero che gli state dicendo che sta nella folder Controllers, ma lo vedremo tra poco. Sta di fatto che quando arriva una richiesta http che deve essere girata a quel controller l’engine di MVC va dalla DefaultControllerFactory e gli chiede di risolvere il problema: voglio questo controller, mo arrangiati un po’ te :-).
Come possiamo metterci in mezzo? semplicissimo:
var container = new WindsorContainer();
var factory = new WindsorControllerFactory( container );

ControllerBuilder.Current.SetControllerFactory( factory );
dove una WindsorControllerFactory altro non è che:
class WindsorControllerFactory : DefaultControllerFactory
{
    IWindsorContainer container;
    public WindsorControllerFactory( IWindsorContainer container )
    {
        this.container = container;
    }

    protected override IController GetControllerInstance( RequestContext requestContext, Type controllerType )
    {
        return container.Resolve( controllerType ) as IController;
    }
}
triviale è dir poco :-)
DefaultNamespaces
Ma cosa succede in fase di risoluzione del “nome” del controller? una volta fatta la prima parte, iniettato c'ioè il nostro motore di IoC preferito, se piazziamo un bel breakpoint in quel metodo GetControllerInstance ci accorgiamo che il tipo che arriva come secondo parametro è il System.Type del controller che MVC ritiene debba soddisfare la richiesta.
Il problema ce ci poniamo è: ma come fa questa scelta? l’unica cosa che sa è che a fronte di una richiesta http deve girare l’onere ad un controller che si chiama Home. Quello che succede è più o meno questo, e qui eventualmente lascio intervenire Simone per i dettagli:
  • una volta scoperto che il controller è Home, lo “scopre” grazie alla RouteTable;
  • parte la ricerca, via reflection, per una classe HomeController:
    • che erediti da Controller;
    • che si trovi nello stesso assembly dell’applicazione MVC;
    • e che, presumo, abbia un full type name: “Application Default Namespace” + Controllers + “Controller Name
se questa convenzione non viene rispettata il giochetto non funziona. Quindi come possiamo spostare i controller in un posto diverso?
ControllerBuilder.Current.DefaultNamespaces.Add( "ManagedDesigns.Next.Mvc.Runtime.Controllers" );
Questo altro non fa che dare la possibilità al motre di MVC di cercare i controller anche li, dove quel li può essere un assembly separato, a patto che stia nella bin dell’applicazione, permettendoci di fare ad esempio questo, in fase di configurazione di Castle:
container.Register( AllTypes.Of<Controller>()
    .FromAssemblyNamed( "Next.Mvc.Runtime" )
    .Configure( cd => cd.LifeStyle.Is( LifestyleType.Transient ) ) );
registriamo in maniera implicita tutti i tipi che derivano da Controller e che stanno nell’assembly indicato.
  • Elegante;
  • Semplice;
  • Efficace;
.m