WCF e Inversion-of-Control: un’altra possibile via di convivenza
Quella soluzione soffre purtroppo di un inconveniente che, a mio modo di vedere, è tutto tranne che trascurabile.
Piccolo ripassino…
Prima di indagare e disquisire facciamo un breve ripasso su come gira il fumo, altresì noto come ciclo di vita di un servizio WCF e attori coinvolti; partendo dal fondo, mettendoci quindi nei panni del servizio WCF:
- IInstanceProvider: l’istanza di un servizio WCF è in ultimo fornita (e gestita) da una classe che implementa l’interfaccia IInstanceProvider; Un IInstanceProvider è paragonabile ad un container, ha due simpatici metodi GetInstance e ReleaseInstance che sono decisamente auto esplicativi;
- IServiceBehavior: come fa un IInstanceProvider a infilarsi nella pipeline di WCF? lo fa tramite un behavior, per l’esattezza un IServiceBehavior applicato a uno o più endpoint;
- ServiceHost: un IServiceBehavior può a sua volta essere inserito nella pipeline di WCF in 2 modi:
- tramite file configurazione, ma dovete pagare lo scotto che non avete accesso al processo di creazione dell’istanza del behavior e rischiate di dover ricorrere al famigerato “service locator pattern”;
- tramite un ServiceHost custom che in fase di apertura dell’host altro non fa che aggiungere il behavior allalista di quelli da usarsi;
- ServiceHostFactory: ok…ma se sono ad esempio hostato in IIS, e quindi ho un file svc, come posso avere controllo sulla creazione del ServiceHost? semplice… tramite una ServiceHostFactory custom che è il primo vero entry point nel processo di creazione di tutto il mondo;
out-of-the-box avete fondamentalmente due modi per agganciare una ServiceHostFactory alla pipeline di WCF:
- Mettendo mano al markup del file .svc e aggiungendo l’attributo “Factory”, consentendovi di specificare una factory specifica per ogni servizio:
<%@ ServiceHost Language="C#"
Service="MyWebApplication.MyService"
Factory="MyWebApplication.MyServiceHostFactory" %>
- Oppure definendo nel file di configurazione della vostra applicazione quale deve essere la factory di default per ogni servizio:
Bello…ma…<system.serviceModel>
<serviceHostingEnvironment>
<serviceActivations>
<add service="MyWebApplication.MyService"
relativeAddress="MyService.svc"
factory="MyWebApplication.MyServiceHostFactory"/>
serviceActivations>
serviceHostingEnvironment>
system.serviceModel>
…non è tutto oro quello che luccica, nel mondo di Inversion of Control e della Dependency Injection c’è una regola aurea: “deve esistere uno ed un solo container”.
Il modello di WCF prevede che venga, per certi versi giustamente, creata un’istanza della factory per ogni servizio che esponete, ma a questo punto nasce un inghippo…dato che, in entrambi i casi di cui sopra, la factory viene esplicitata in maniera dichiarativa non avete nuovamente controllo sulla sua creazione e se volete avere a disposizione un container per Inversion of Control al fine di gestire il ciclo di vita dei vostri servizi siete di nuovo costretti a ricorrere nuovamente a qualcosa basato su “service locator” o porcate simili che implicano l’uso di static…mannaggia…
.Net 4.0 e WCF 4.0: si… può… fare!
vediamo subito il come poi capiamo il cosa; nel vostro simpatico Global.asax potete fare una cosa del tipo:
ApplicationBootstrapper non è altro che un wrapper per la creazione e l’inizializzazione di Castle Windsor via MEF, la cosa veramente interessante sono le ultime due righe:ApplicationBootstrapper bootstrapper;
protected void Application_Start( object sender, EventArgs e )
{
var root = AppDomain.CurrentDomain.BaseDirectory;
var directory = Path.Combine( root, "bin" );
this.bootstrapper = new ApplicationBootstrapper( directory );
var container = this.bootstrapper.Boot();
var hostFactory = new Hosting.MyServiceHostFactory( container );
RouteTable.Routes.Add( new ServiceRoute( "my", hostFactory, typeof( MyService ) ) );
}
- creiamo manualmente (avremmo anche potuto farla risolvere al container) una factory custom a cui passiamo nel costruttore un’istanza del nostro fido container;
- sfruttiamo una figata del motore di routing: le ServiceRoute. Una ServiceRoute non è altro che una route speciale che è in grado di attivare un servizio WCF…come?
- usando come prefisso della rotta il valore che decidiamo noi, in questo esempio “my”;
- usando una factory fornita dall’esterno, in questo caso la nostra;
- specificando quale sia il tipo di servizio che quella rotta esporrà;
.m