Passare un Expression di Entity Framework attraverso un servizio WCF

ADO.NET Entity Framework è un ORM targato Microsoft che tra le principali caratteristiche ha il grande punto di forza di poter essere utilizzato con LINQ to Entities per facilitare l'interrogazione e la manipolazione dei dati. Questo è possibile grazie a LINQ e in particolare all'interfaccia IQueryable che permette di creare un grafo di oggetti di tipo Expression che sono in grado di rappresentare un espressione al fine di consentire a motori, come LINQ to SQL o LINQ to Entities, di trasformarlo in una query ANSI SQL.

Il difetto di questo approccio è che tale espressione, e in generale LINQ, sono legati alla tecnologia .NET e non possono essere trasportati al di fuori di questo mondo. Per esempio, se si sviluppa un servizio WCF che permette l'interrogazione dei prodotti, è necessario che si definisca un'operazione per ottenere una lista ed eventualmente fornire come parametro una struttura che permetta di filtrare per uno o più campi, di ordinare o di paginare. Qualunque scelta si faccia, è molto probabile che la struttura definita sia comunque limitata e non adatta a coprire tutte le esigenze che i potenziali consumer del servizio possano aver bisogno.

Sul sito di MSDN è a disposizione all'indirizzo http://code.msdn.microsoft.com/exprserialization del codice di esempio che mostra come serializzare e deserializzare in XML un Expression, perciò l'idea di questo script è di proporre di usare tale codice per creare un servizio che riceva l'espressione della query da creare e la esegua con LINQ to Entities (nell'esempio MSDN viene usato LINQ to Sql).

Si prenda in esame questo codice che effettua una normale query:

NorthwindEntities ne = new NorthwindEntities();
// Preparazione della query da effettuare
string s = "Italy";
IQueryable query = ne.ProductSet.Where(p => p.UnitPrice > 0 && p.Suppliers.Country == s);

La proprietà query.Expression rappresenta il grafo di oggetti dell'espressione che non è ancora stata eseguita. Il ToString restituisce il seguente simil codice C#:

value(System.Data.Objects.ObjectQuery`1[EFWebApplication.Product]).Where(p => ((p.UnitPrice > Convert(0)) && (p.Suppliers.Country = value(EFWebApplication._Default+<>c__DisplayClass0).s)))

La parola value sta ad indicare una costante che in questo caso è ObjectQuery. Questa ovviamente non può essere serializzata, ma è possibile solo mantenere il fatto che si intende interrogare l'ObjectQuery dei prodotti. Da un punto di vista consumer del servizio infatti, quello che conta è che si vuole prendere i prodotti il cui UnitPrint è maggiore di zero e il Country sia Italy. Nel codice precedente è stata usata una variabile perciò l'espressione fa riferimento ad essa tramite un MemberExpression. Anche questa informazione non può essere trasformata in XML (il codice client non può essere portato sul server), perciò in fase di serializzazione il motore deve interrogare la variabile e la converte in una costante.

Ecco quindi come risulta facile, con le classi incluse nello script, serializzare una query:

// Creazione serializzatore delle espressioni
ExpressionSerializer serializer = new ExpressionSerializer();
// Convertitore speciale apposito per Entity Framework
serializer.Converters.Add(new EFCustomExpressionXmlConverter(ne, serializer));

// Serializzo l'expression in xml
xmlExpression = serializer.Serialize(query.Expression);

Di seguito invece come deserializzare l'espressione e creare una query per l'entità Product:

// Deserializzo l'expression dall'xml
Expression ex = serializer.Deserialize(xmlExpression);

// Creo la query sulla base dell'expression deserializzata
IQueryable<Product> r = ((IQueryable)ne.CreateQuery<Product>("*")).Provider.CreateQuery<Product>(ex);

Il limite principale di questa tecnica risiede che non si possono effettuare query complesse laddove vengono valutate classi presenti sul consumer. Queste infatti non possono essere serializzate e si può fare solo riferimento a variabili calcolate prima della preparazione dell'expression. Non è possibile inoltre usare delegate ad eccezione delle lampda e non è possibile fare query miste con LINQ to *.

Con questa tecnica un servizio può ricevere come parametro l'XML dell'espressione oppure, per automatizzare il tutto, si può valutare l'uso di un DataContractSerializerOperationBehavior per specificare il surrogate per la serializzazione tramite DataContractSerializer dell'Expression o lavorare direttamente con un IClientMessageInspector/IDispatchMessageInspector per intervenire sull'XML SOAP.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi