MVC Web Api e IHttpControllerActivator Dependency Injection

In questi giorni sto lavorando ad un progetto interessante che prevede un massiccio uso di ASP.NET Web API.
Così, come ormai ad ogni startup di progetto, ci prepariamo ad architettare quel qualcosa 🙂 che ci permetta di iniettare dove necessario le giuste dipendenze allo scopo di semplificare la fase di test dei prodotti finali delle nostre svariate elucubrazioni mentali 😀 …

Dopo una serie di considerazioni fatte, sulle quali non mi dilungherò,  decidiamo di utilizzare MS Unity come IoC Container e ci lanciamo nell’ormai classica implementazione di un ControllerFactory per intercettare la creazione dei Controller per l’applicazione ASP.NET MVC4 come segue

public class UnityControllerFactory : DefaultControllerFactory
{
    private readonly IUnityContainer _container;

    /// <summary>
    /// Base Constructor
    /// </summary>
    ///Instance of Container.
    public UnityControllerFactory(IUnityContainer container)
    {
        _container = container;
    }

    /// <summary>
    /// Returns a valorized instance of the container.
    /// </summary>
    /// <param name="requestContext">Information about HTTP Request.</param>
    /// <param name="controllerType">Controller Type.</param>
    /// <returns></returns>
    protected override IController GetControllerInstance(RequestContext requestContext,
                                                         Type controllerType)
    {
        Contract.Requires(requestContext != null, "requestContext");
        Contract.Requires(controllerType != null, "controllerType");

        return _container.Resolve(controllerType) as IController;
    }

    /* Ulteriori override di nostra necessità...
     * protected override ...
     * {
     *    ...
     * }
    */
}

Nulla di nuovo quindi e procediamo a registrare il nostro container nel global.asax

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        var container = this.GetContainer("unity");
        var unityControllerFactory = new UnityControllerFactory(container);
        ControllerBuilder.Current.SetControllerFactory(unityControllerFactory);
    }

    public IUnityContainer GetContainer(string name)
    {
        IUnityContainer container = new UnityContainer();

        object section = ConfigurationManager.GetSection(name);
        if (section == null)
            throw new InvalidOperationException("No unity configuration was found, could not instansiate container");

        UnityConfigurationSection unityConfigurationSection = section as UnityConfigurationSection;
        unityConfigurationSection.Configure(container);

        return container; 
    }
         
}

Ma purtroppo ci rendiamo subito conto che l’implementazione del nostro UnityControllerFactory non ci permette di intercettare la creazione degli ApiCotroller e di conseguenza neanche di iniettare le nostre dipendenze in questi ultimi… la soluzione?

Abbiamo bisogno di un UnityHttpControllerActivator (per ulteriori informazioni leggi qui)

Poche righe ancora quindi per intercettare la creazione i nostri WebApiController

public class UnityHttpControllerActivator : IHttpControllerActivator
{
    private readonly IUnityContainer _container;
    private readonly DefaultHttpControllerActivator _defaultActivator;

    public UnityHttpControllerActivator(IUnityContainer container)
    {
        _container = container;
        _defaultActivator = new DefaultHttpControllerActivator();
    }

    public IHttpController Create(System.Net.Http.HttpRequestMessage request, 
                                  HttpControllerDescriptor controllerDescriptor, 
                                  Type controllerType)
    {
        IHttpController httpController = _container.Resolve(controllerType) as IHttpController;
        if (httpController != null)
        {
            return _container.Resolve(controllerType) as IHttpController;
        }
        else
        {
            return _defaultActivator.Create(request, controllerDescriptor, controllerType);
        }
    }
}

a questo punto dobbiamo aggiungere alla funzione Application_Start nel global.asax

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        var container = this.GetContainer("unity");
        var unityControllerFactory = new UnityControllerFactory(container);
        ControllerBuilder.Current.SetControllerFactory(unityControllerFactory);

        var unityHttpControllerActivator = new UnityHttpControllerActivator(container);
        GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), 
                                                           unityHttpControllerActivator);
    }
    
    ...
}

Bene! ..ed ora iniettate tutte le dipendenze di cui avete bisogno e buon lavoro! 😀

Minimizzare le dipendenze

Una delle problematiche principali che spesso impediscono la realizzazione di applicazioni modulari e facilmente estendibili è rappresentato dalle dipendenze esistenti tra i componenti che le costituiscono. Eliminare queste dipendenze vuol dire poter godere di innumerevoli vantaggi come la possibilità di effettuare test automatici ed avere un codice più incline al cambiamento. Negli ultimi anni, la marcata diffusione di framework come Spring, ha portato l’attenzione di un gran numero di sviluppatori su interessanti concetti quali la gestione dell’IoC (Inversion of Control), l’implementazione di tecniche relative alla AOP (Aspect Oriented Programming) e servizi di Service Locator Transparency che, se ben applicati, ci aiutano a minimizzare le dipendenze tra le entità di un sistema.

Ma cerchiamo di chiarire il concetto di dipendenza:

  1. “Una classe A dipende da una classe B quando: A eredita da B”
  2. “Una classe A dipende da una classe B quando: A crea un istanza e/o utilizza un istanza di B ”
  3. “Una classe A dipende da una classe C quando: B dipenda da C ed A dipende da B”

Inversion of Control (o IoC)
Solitamente nello scrivere applicazioni di una certa complessità spesso si finisce ad avere frequenti casi di dipendenza tra le classi di un Domain Model, questo accade perché, in generale, si rendono le classi stesse responsabili della propria inizializzazione e soprattutto nel costruttore, o in un metodo di inizializzazione preposto, si fa si che la classe istanzi tutto ciò di cui ha bisogno per funzionare correttamente. In parole povere è il codice stesso che ha il “controllo” di come vengono inizializzati gli oggetti. In progetti di elevata complessità tutto questo porta spesso ad avere diversi problemi in fase di refactory dell’architettura applicativa. Possiamo per esempio immaginare quei casi in cui, a fronte di nuove esigenze, si abbia la necessità di modificare il processo di inizializzazione di alcune entità del nostro modello, ovviamente ciò comporta il dover “mettere mano” al codice in tutti quei punti in cui queste vengono istanziate ed inizializzate. Per ovviare a tali problematiche si possono sfruttare dei meccanismi di inizializzazione configurabile, ed è proprio in questo ambito che l’Inversion of Control (IoC) trova il suo campo d’applicazione.

In generale non è detto che un oggetto sia a conoscenza del macro-contesto nel quale sarà utilizzato, ciò significa che un’entità dovrebbe saper comunicare con l’ambiente nel quale viene posta nel modo più generico possibile e potersi, quindi, considerare indipendente dal contesto applicativo corrente. A tal proposito viene introdotto il concetto di iniettore (talvolta chiamato anche assemblatore) ovvero l’entità che dovrebbe occuparsi, di “preparare i componenti già pronti all’uso” su richiesta di un qualsiasi consumer in modo da svincolare quest’ultimo dall’onere di istanziare ed inizializzare le sue dipendenze (leggi classi utilizzate). In secondo luogo bisogna dire che il consumer stesso, essendo un eventuale candidato all’utilizzo da parte di altre entità, non dovrebbe doversi preoccupare neanche della sua inizializzazione.

Progettare un iniettore è un’operazione che in prima analisi più sembrare un operazione alquanto semplice, ma bisognerebbe considerare con attenzione che sviluppare un iniettore troppo semplice potrebbe comportare come unico risultato lo spostamento delle problematiche succitate in un’altro punto dell’applicazione. Un iniettore per avere senso di esistere deve garantire un elevato grado di flessibilità ottenibile con un altrettanto elevato grado di configurabilità.

La Dependency Injection
La Dependency Injection è un design pattern che tenta di risolvere i problemi di dipendenza tra entità di un modello di domino attuando politiche di Inversion Of Control. Con la Dependency Injection una classe o un sistema non è responsabile dell’inizializzazione delle proprie dipendenze ma prevede la presenza di un iniettore che “inietti” tali dipendenze direttamente negli oggetti gestiti.

L’iniettore deve offrire un meccanismo di configurazione utilizzato per la definizione degli oggetti conosciuti e di come questi siano correlati tra di loro; alla richiesta di un dato oggetto l’iniettore controlla quali altre classi sono ad esso collegate, le crea, e le inietta al suo interno prima di rendere l’oggetto disponibile. Arrivati a questo punto, potremmo quasi considerare le classi del nostro domain model come dei dispensatori di servizi e l’iniettore come un Service Locator particolare in quanto oltre ad individuare le classi svolge anche il compito non banale di inizializzarle e renderle pronte all’uso.

NOTE:

In generale per minimizzare le dipendenze tra oggetti è necessario che questi dialoghino tra loro tramite interfacce (o classi astratte per i linguaggi che le supportano), in questo modo l’oggetto dipendente (un oggetto A ad esempio) può regredire al grado di semplice utilizzatore senza doversi più preoccupare di conoscere le modalità con le quali la sua dipendenza (intesa come un oggetto B dal quale dipende A) viene instanziata e inizializzata. Potremmo dire  che l’oggetto A, a questo punto, sa che dialogherà con qualcuno (che conosce come IB e non come B, ovvero conosce la sua interfaccia e non una sua speciale implementazione) e che questo qualcuno saprà come portare a termine determinate operazioni ma senza sapere questo qualcuno chi sia (A conosce cosa c’è da fare ma non chi e come lo farà). L’oggetto B, quindi, sarà costretto ad implementare un interfaccia (un comportamento da garantire, IB appunto).

Una dependency è un modo diverso di chiamare le variabili di un classe, i suoi campi, o se si preferisce la variabili di istanza che dipendono da tipi non appartenenti al core standard del linguaggio utilizzato.

Il procedimento di injection è l’inizializzazione di una variabile (della dependency), ciò avviene quando all’esterno della classe che contiene la dependency  si passa la dependency stessa già inizializzata che può avvenire tramite costruttore, tramite proprietà (interfaccia) o tramite metodo setter.

IoC Container
L’importanza dell’implementazione sempre crescente dei meccannismi di IoC e di DI in progetti reali ha portato negli ultimi anni allo sviluppo di svariati framework per semplificarne l’utilizzo (un famoso esempio ne è Srping), tali framework sono considerati come IoC Container ovvero una sorta di repository dove a fronte di un’adeguata configurazione possono essere referenziati dei mapping sui tipi i nmodo che il framework possa su richiesta dei consumer scovare le dipendenze instanziarle e iniettarle al chiamante. Definendo quindi delle interfacce comportamentali, ed implementandole nelle nostre classi concrete, risulta possibile accoppiare le classi a run-time senza più porsi il problema di conoscere le entità concrete a compile-time.