Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Un framework di Dependency Injection per isolare il Controller

Semplificare l'isolamento del controller utilizzando Unity, un semplice framework per la DI
Semplificare l'isolamento del controller utilizzando Unity, un semplice framework per la DI
Link copiato negli appunti

La tecnica illustrata nella lezione precedente viene chiamata Dependency Injection, termine coniato da Martin Fowler, ed è uno dei servizi base di molti framework che implementano il pattern Inversion of Control (IoC) in cui le dipendenze tra layer applicativi sono risolte a run-time chiedendo ad un terzo attore, di solito una factory, di ottenere un'istanza della classe di cui abbiamo bisogno.

Il gruppo Patterns & Practices di Microsoft ha rilasciato, insieme alle sue famose Enterprise Library, un framework di IoC chiamato Unity, grazie al quale con poche semplice istruzioni possiamo rendere le dipendenze tra i nostri layer un parametro di configurazione.

Una volta scaricata e installata la libreria ci basta istruire la nostra applicazione su come trattare la dipendenza.

Se decidiamo di usare il web.config per gestire tale configurazione possiamo aggiungere una config section apposita:

<configSections>
  <section name="unity" 
           type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>

Per spiegare a Unity come risolvere il nostro ITestiWebServices a questo punto ci basta associare l'interfaccia all'implementazione che vogliamo utilizzando un elemento type:

<unity>
 <containers>
  <container>
   <types>
    <type type="ApplicationCore.Interfaces.Web.ITestiWebServices, ApplicationCore"
          mapTo="ApplicationServices.Web.TestiWebServices, ApplicationServices" />
   </types>
  </container>
 </containers>
</unity>

A questo punto possiamo chiedere a Unity di restituirci un'implementazione di ITestiWebServices che concretizzerà un'istanza di TestiWebServices. Il nostro costruttore diventerà quindi:

public ChiSiamoController()
{
  IUnityContainer container = new UnityContainer();
  UnityConfigurationSection section = (UnityConfigurationSection) ConfigurationManager.GetSection("unity");
  section.Containers.Default.Configure(container);
  this.testiServices = (ITestiWebServices)container.Resolve(typeof(ITestiWebServices));
}

Mentre il secondo costruttore a questo punto può essere eliminato.

Bello? Si, ma possiamo fare di più, considerando che la factory di ASP.NET MVC, che restituisce le classi controller in base alla richiesta pervenuta, può essere personalizzata! Estendiamo quindi la DefaultControllerFactory di MVC ed eseguiamo l'override del metodo GetControllerInstance:

public class UnityControllerFactory : DefaultControllerFactory
{
  IUnityContainer _container;     
  public UnityControllerFactory()
  {
    if(_container == null) _container = new UnityContainer();
    UnityConfigurationSection section = (UnityConfigurationSection) ConfigurationManager.GetSection("unity");
    section.Containers.Default.Configure(_container);
  }
  protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
  {
    return _container.Resolve(controllerType) as IController;
  }
}

La nostra classe non fa altro che mantenere un'istanza del container unity e risolvere i controller quando questi vengono richiesti. Per dire alla pipeline di ASP.NET MVC di usare il nostro controller anziché quello di default basta aggiugere all'Application_Start del global.asax le istruzioni:

IControllerFactory controllerFactory = new UnityControllerFactory();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);

In questo modo possiamo eliminare dal nostro controller anche il costruttore vuoto, decorando con un attributo Dependency la property che rappresenta il servizio:

public class ChiSiamoController : Controller
{
  [Dependency]
  public ITestiWebServices TestiServices { get; set; }
  public ActionResult Index()
  {
    TestoDTO testo = this.TestiServices.RecuperaTesto("chisiamo");
	return View(testo);
  }
}

Infatti quando la nostra factory chiederà a Unity di risolvere il tipo ChiSiamoController, che non abbiamo aggiunto al nostro file di configurazione, il framework si preoccuperà di risolvere anche le property decorate, nel nostro caso TestiServices, rendendo le nostre implementazioni semplici ed eleganti.

Ti consigliamo anche