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

Un po' di free-style con Linq e gli Expression Tree

Uno dei vantaggi più graditi di Linq sono gli Expression Tree, una vera manna per sistemare i classici problemi di condizioni where non prevedibili durante la progettazione.

Gli Expression Tree sono compilati, strong-typed, provider independent e serializzabili (anche se con qualche aiutino).

Prendiamo l'esempio di un semplice accesso ai dati usando Linq2SQL, il mio DAL preferito.

   1: public static IEnumerable<Article> GetList(Expression<Func<AnaArt, bool>> Filter)
   2: {
   3:     var ctx = GetDataContext();
   4:     var items = ctx.AnaArts.Where(Filter).Select(c => new Article(c.MGAA_Id, c.MGAA_MBDC_Classe, c.MGAA_Matricola, c.MGAA_Descr));
   5:     return items;
   6: }
   7:  
   8: public static int GetListCount(Expression<Func<AnaArt, bool>> Filter)
   9: {
  10:     var ctx = GetDataContext();
  11:     var res = ctx.AnaArts.Count(Filter);
  12:     return res;
  13: }

La GetList usa un predicate che ha in ingresso l'i-esimo oggetto mappato ad un record e in uscita c'è il boolean che decide se quel record debba essere incluso o meno nel resultset. Func è il delegate, Expression<Func<..>> è l'Expression Tree che usa quel delegate. L'Expression Tree è quello che permette la "magia" di tradurre il predicate in linguaggio SQL a carico del provider.

La GetListCount fa esattamente lo stesso ma si avvale di Count che, come la where, accetta un predicate.

  • Creando il predicate al volo con PredicateBuilder di Joe Albahari di cui raccomando LinqPad, fantastico tool ora dotato anche di Intellisense da usarsi al posto del classico Query Analyzer e per rendersi conto se la query Linq è scritta bene o rischia di fare disastri in performance sul DB (che è sempre colpa di una query scritta male).

    Per dare l'idea di un caso pratico, in una applicazione per un cliente abbiamo creato un designer visuale in WPF di condizioni Where in modo che l'utente potesse decidere il criterio di scelta. Poi abbiamo serializzato i predicate in modo 'custom' e le ricerche possono essere ricaricate e riutilizzate con estrema semplicità.
    Per chi sia interessato a serializzare, esiste un serializzatore di Expression Tree realizzato da una persona del team di C#
  • In alcuni casi la where la si può prevedere facilmente e quindi basta creare un filtro ad-hoc, per esempio recuperando il record con un certo Id:
   1: public static Article GetArticleById(int Id, bool IsCount)
   2: {
   3:     Expression<Func<AnaArt, bool>> Filter = c => c.MGAA_Id == Id;
   4:     
   5:     return GetArticles(Filter).FirstOrDefault();
   6: }

Bello, vero ma ci sono casi in cui queste where sono solo dei "pezzi" che farebbe comodo riutilizzare in diversi contesti e magari unire, grazie al predicatebuilder, con altri "pezzi" di where.

In sostanza, tanto per fare un esempio semplice, sarebbe più comodo ed elegante che il metodo usasse la funzione FilterById.

Attenzione però che non può trattarsi di una normale funzione C#. Il nostro requisito indispensabile è che sia un Expression Tree traducibile in linguaggio SQL a cura del provider. Quindi FilterById deve essere un Expression<Func<...>>.

   1: public static Article GetArticleById(int Id, bool IsCount)
   2: {
   3:     Expression<Func<AnaArt, bool>> Filter = FilterById(Id);
   4:     
   5:     return GetArticles(Filter).FirstOrDefault();
   6: }

In pratica mi serve una funzione che:

- in ingresso prenda un intero (Id)
- in uscita mi restituisca un Expression<Func<...>>, cioè quello che la where si aspetta

  • Ed ecco il risultato (opzione 3):
   1: public static readonly Func<int, Expression<Func<AnaArt, bool>>> FilterById = Id => (d => d.MGAA_Id == Id);

FilterById è una lambda dove:

- Id è il parametro di ingresso (un intero)
- (d => d.MGAA_Id == Id) è una lambda in uscita

Questa lambda in uscita ha la forma di Expression<Func<AnaArt, bool> cioè:

- in ingresso prende l'i-esimo id del record che verrà processato e lo compara con l'intero che gli è arrivato come parametro dalla lambda precedente (quindi ormai è noto)
- in uscita restituisce un boolean che dice se includere o meno il record nel resultset

Niente di nuovo, e chi ha già giocato con Linq avrà sicuramente esplorato queste belle cose, ma ripeterle non fa mai male.

Print | posted on venerdì 17 aprile 2009 13:22 |

Feedback

Gravatar

# re: Un po' di free-style con Linq e gli Expression Tree

Gli expression tree sono fantastici, io mi ero costruito un miniparser che prende una stringa di condizioni, la splitta e poi da li crea un expression tree per la condizione. In questo modo uno può scrivere Customer.Name == "Pippo" e poi grazie alla traduzione in expression tree si può passare a LINQ2SQL o a LINQ2NHibernate.

alk.
25/04/2009 14:22 | Gian Maria
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET