posts - 644, comments - 2003, trackbacks - 137

My Links

News

Raffaele Rialdi website

Su questo sito si trovano i miei articoli, esempi, snippet, tools, etc.

Archives

Post Categories

Image Galleries

Blogs

Links

Usare MEF in WCF

Il Managed Extensibility Framework è un potente framework che permette di estendere in modo semplice le nostre applicazioni. È parte integrante del Framework 4.0 ed è disponibile al download per la versione 3.5 a questo link dove si trovano anche esempi molto interessanti, documentazione e discussioni proficue.

Lo scenario che ho voluto indirizzare è di un servizio noto che vuole utilizzare dei plugin esterni. Tanto per capirci non è uno scenario (peraltro possibile) nel quale gli addin sono i servizi (implementazione del contrratto WCF) da esporre.

Uno dei cardini di MEF è il catalog che si occupa di mantenere le informazioni degli assembly delle estensioni (le dll non referenziate all'applicazione principale che verranno caricate ed eseguite da MEF a runtime). Per questo motivo il catalog tipicamente ha lo stesso ciclo di vita della nostra applicazione.

Mentre nelle applicazioni web il ciclo di vita può essere mantentenuto in HttpApplication nel global.asax o in un modulo, per le applicazioni WCF le cose sono un po' diverse, soprattutto perché potremmo decidere di hostare il servizio WCF anche senza IIS.

La soluzione che ho adottato nel progetto su cui sto lavorando è quella di creare un semplice Instance Provider di WCF che mantiene in vita il catalog di MEF ed esegue le operazioni basilari (popolare gli imports del servizio). Il modo più 'pulito' per realizzare un instance provider è quello di realizzare un behavior sotto forma di attributo.

Il risultato finale sarà perciò solo di decorare il nostro servizio con un nostro attributo chiamato MEFBehavior:

[MEFBehavior] public class MyService : IMyService { ... }

La realizzazione del behavior è molto semplice e si limita all'implementazione di un paio di interfacce. Il behavior ha sostanzialmente solo lo scopo di creare l'instance provider.

public class MEFBehaviorAttribute : Attribute, IContractBehavior, IContractBehaviorAttribute { public Type TargetContract { get { return null; } } public void AddBindingParameters(ContractDescription description, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection parameters) { } public void ApplyClientBehavior(ContractDescription description, ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior(ContractDescription description, ServiceEndpoint endpoint, DispatchRuntime dispatch) { dispatch.InstanceProvider = new MEFInstanceProvider(); } public void Validate(ContractDescription description, ServiceEndpoint endpoint) { } }

La proprietà TargetContract ritorna null in modo da applicarsi a qualsiasi contratto, come specificato qui.

L'instance provider si occupa di creare il catalog una sola volta e poi di soddisfare i requisiti MEF del servizio ad ogni istanza creata, il tutto condito con un po' di logica di sincronizzazione per evitare spiacevoli potenziali inconvenienti da accesso multi-threading che potrebbero verificarsi nonostante il "true" nel secondo parametro del CompositionContainer (che indica di volere la thread safety):

public class MEFInstanceProvider : IInstanceProvider { private static CompositionContainer _container; private static object _synclock; static MEFInstanceProvider() { _synclock = new object(); Compose(); } public object GetInstance(System.ServiceModel.InstanceContext instanceContext, System.ServiceModel.Channels.Message message) { var serviceType = instanceContext.Host.Description.ServiceType; var instance = Activator.CreateInstance(serviceType); ComposeInstance(instance); return instance; } public object GetInstance(System.ServiceModel.InstanceContext instanceContext) { return GetInstance(instanceContext, null); } public void ReleaseInstance(System.ServiceModel.InstanceContext instanceContext, object instance) { var disposable = instance as IDisposable; if (disposable != null) disposable.Dispose(); } private void ComposeInstance(object instance) { lock (_synclock) { CompositionBatch batch = new CompositionBatch(); batch.AddPart(instance); try { _container.Compose(batch); } catch (CompositionException compositionException) { Trace.WriteLine(compositionException.ToString()); } catch (Exception err) { Trace.WriteLine(err.ToString()); } } } private static void Compose() { var catalog = new AggregateCatalog(); var layoutFolder = ConfigurationHelper.GetLayoutFolder().FullName; catalog.Catalogs.Add(new DirectoryCatalog(layoutFolder)); _container = new CompositionContainer(catalog, true); } }

 

ConfigurationHelper.GetLayoutFolder è un mio helper per recuperare la cartella degli addin dagli appSettings della configurazione.

Tutto il resto è esattamente uguale a ciò che viene fatto nelle applicazioni che usano MEF. Ad esempio il servizio esporrà un membro con la collection di plugins in modo simile a questo:

[ImportMany] private Lazy<IPrintDataProcessor, IPrintCategory>[] PrintModules { get; set; }

Semplice e potente, non si può proprio dire altro!

Print | posted on martedì 8 febbraio 2011 10:59 |

Feedback

Gravatar

# re: Usare MEF in WCF

Sapevo che non finiva cosi' (post di ieri).
Bella idea.
08/02/2011 15:20 | raffaeu
Gravatar

# re: Usare MEF in WCF

@felix. Se intendi un webservice asmx, dovresti cavartela con un httpmodule che mantiene in application il catalog. Dal module puoi intercettare la creazione dell'istanza del servizio (mi viene in mente di sostituire la factory dell'handler asmx) per popolare gli imports.
Personalmente convertirei asmx in wcf non per smania di usare tecnologie più recenti ma perché il wsdl di asmx è debole e può corrompere i dati (senza eccezioni) in caso di messaggio corrotto.

@raffaeu Grazie! :)
08/02/2011 19:41 | Raffaele Rialdi
Gravatar

# re: Usare MEF in WCF

lo farei volentieri, il fatto è che l'asmx in questione gira sotto mod_mono su ambiente "ostile" ;) i wcf ho provato a farglieli digerire ma non ci sono ancora riuscito completamente e cmq non credo che sotto mono i wcf mi assicurino messaggi non corrotti.
Cmq grazie per le indicazioni.
Ciao F.
10/02/2011 15:18 | felix
Gravatar

# re: Usare MEF in WCF

@felix. Non sono ancora riuscito a fare prove di WCF in Mono. Comunque se hanno implementato il soap parlato da wcf, i messaggi sono più robusti di conseguenza, quindi sono ottimista.
10/02/2011 18:28 | Raffaele Rialdi
Gravatar

# re: Usare MEF in WCF

In poche parole, io ho il mio WCF con i suoi contratti ecc, dentro 1 di questo voglio che venga dato al client l'elenco dei plugin che ci sono, e poi il client selezionerà quello che vuole eseguire
07/02/2012 19:26 | Roberto
Gravatar

# re: Usare MEF in WCF

con i sui servizi non contratti*
07/02/2012 19:28 | Roberto
Gravatar

# re: Usare MEF in WCF

Quanto racconti assomiglia più ad una 'discovery' (vedi specs WS-Discovery) di altri servizi, cosa che WCF4 già gestisce con due distinte modalità.
In pratica tu ha un servizio al quale ti rivolgi per sapere dove si trova un altro servizio con certe caratteristiche. Una volta recuperate le info, procedi a chiamare il servizio che hai appena scoperto.
07/02/2012 20:07 | Raffaele Rialdi
Gravatar

# re: Usare MEF in WCF

non mi riesco a spiegare, ho provato a fare quello che intendevo io con il codice riportato qui ma non riesco a capire dove inserire tutti i vari pezzi di codice
08/02/2012 10:56 | Roberto
Gravatar

# re: Usare MEF in WCF

Il codice che ho postato fa una cosa diversa da quella che chiedi. Per la precisione serve a questo:
- Il contratto wcf è fisso
- Il servizio wcf necessità, per esempio, di fare un pre-processing o post-processing dei dati utilizzando un plugin esterno che viene appunto caricato con MEF
- il contratto MEF viene condiviso tra il servizio wcf e gli autori dei plugin
- il servizio wcf dichiara una proprietà con [ImportMany] per caricare la lista dei plugin disponibili. Grazie al behavior/instanceprovider che ho postato qui, mef carica i plugin utilizzando il "catalog" che preferisce (tipicamente un DirectoryCatalog)
- i plugin marcano la classe che implementa il contratto mef con [Export]
- il servizio seleziona il plugin per nome (stringa passata dal caller del servizio o con qualsiasi altro criterio tu voglia... potrebbe anche chiamarli tutti in sequenza) e chiama il plugin per fargli fare il suo lavoro.

In questo modo WCF ha un contratto fisso e una sola implementazione ma si avvale di una serie di plugin che possono essere caricati e invocati dinamicamente.
08/02/2012 11:33 | Raffaele Rialdi
Gravatar

# re: Usare MEF in WCF

Ok, forse ora ho capito anche io quello che volevo xD però non riesco a farlo funzionare
08/02/2012 11:44 | Roberto
Gravatar

# re: Usare MEF in WCF

Ho risolto! son riuscito a fare quello che volevo! Grazie mille!!
08/02/2012 17:53 | Roberto
Gravatar

# re: Usare MEF in WCF

:) ottimo ciao
08/02/2012 18:56 | Raffaele Rialdi
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET