Showing posts with label COMPOSE. Show all posts
Showing posts with label COMPOSE. Show all posts

Tuesday, October 12, 2010

Combinare due query LINQ

Sembra una vita che non riesco più a trovare il tempo per scrivere due righe sul blog… Colpa del lavoro, a cui sto dedicando decisamente troppo tempo, ok, cerco di rimediare con un mini post.

Vi è mai capitato di avere la necessità di combinare due query LINQ? Sembra una baggianata (e in effetti lo è) ma io ho avuto difficoltà a trovare un esempio bello e pronto… Io ho incontrato il caso con i DomainService, ove mi è servito combinare alla query proveniente dal client, degli altri “pezzi” definiti sul server (si tratta di un evoluzione basata sui due post precedenti sui WCF Ria Services e nHibernate, di cui penso prima o poi di scrivere un post) ma la problematica è decisamente generica.

Definite due query, una che chiamerò “sorgente” è quella al cui queryProvider chiederò di eseguire il risultato della composizione e l’altra, “destinazione”, contiene i “pezzi” che voglio aggiungere alla sorgente.

Per far ciò è sufficiente far “visitare” l’espressione “destinazione” (ovvero quella da aggiungere) da un ExpressionVisitor istanziato sull’espressione sorgente e chiedere al queryProvider “sorgente” di creare una nuova query a partire dall’espressione “visitata”.

Ok, qui servirebbero pagine e pagine di spiegazione su LINQ, ma c’è tanto materiale in giro, e così ben fatto che non mi azzardo nemmeno a scrivere due righe… :-)

…Però il codice necessario lo posto subito!

Come dimostrazione ho creato una semplicissima applicazione console

 using  System;

 using  System.Collections.Generic;

 using  System.Linq;

 using  System.Text;

 

 namespace  ConsoleApplication1

 {

     class  Program 
     { 
         static  void  Main(string [] args)
         {
             List <MyEntity > myList = new  List <MyEntity >()
             {
                 new  MyEntity () 
                 {  
                     Id = 1,
                     MyProperty = "Messina" 
                 },
                 new  MyEntity ()
                 {
                     Id = 2,
                     MyProperty = "Palermo" 
                 },
                 new  MyEntity ()
                 {
                     Id=3,
                     MyProperty="Catania" 
                 }
                 new  MyEntity ()
                 {
                     Id = 4,
                     MyProperty = "Milazzo" 
                 }
             };
             //let's obtain an IQueryable... 
             var  query = myList.AsQueryable();
             //and define a query  over it 
             var  otherQuery = query.Skip(1).Take(2).OrderByDescending(x=> x.Id);
             //print out original count 
             Console .WriteLine("querCount = "  + query.Count().ToString());
             //combine the two queries... 
             var  newQuery = QueryCombiner .Combine<MyEntity >(query, otherQuery);
             //and see something about the result! 
             Console .WriteLine("newQueryCount  = "  + newQuery.Count().ToString());
             foreach  (var  item in  newQuery)
             {
                 Console .WriteLine("rimasti: "  + (item as  MyEntity ).MyProperty);
             }
             Console .ReadKey();
         }
     }
     public  class  MyEntity 
     {
         public  int  Id { get ; set ; }
         public  string  MyProperty { get ; set ; }
     }
 }



che dimostra la piccolissima classe a seguire




 using  System;

 using  System.Collections.Generic;

 using  System.Linq;

 using  System.Text;

 using  System.Collections;

 using  System.Linq.Expressions;

 using  System.ServiceModel.DomainServices.Server;

 

 namespace  ConsoleApplication1

 {

     public  class   QueryCombiner  : ExpressionVisitor 

     {

         // Methods 

         public  static  IQueryable <T> Combine<T>(IQueryable  source, IQueryable  query)

         {

             Expression  expression = Combine(source.Expression, query.Expression);

             return  source.Provider.CreateQuery<T>(expression);

         }

         public  static  IQueryable  Combine(IQueryable  source, IQueryable  query)

         {

             Expression  expression = Combine(source.Expression, query.Expression);

             return  source.Provider.CreateQuery(expression);

         }

 

         public  static  Expression  Combine(Expression  source, Expression  query)

         {

             Expression  retExp = new  Visitor (source).Visit(query);

             return  retExp;

         }

         

         //nested types 

         private  class  Visitor  : ExpressionVisitor 

         {

             // Fields 

             private  Expression  _root;

 

             // Methods 

             public  Visitor(Expression  root)

             {

                 _root = root;

             }

 

             protected  override  Expression  VisitMethodCall(MethodCallExpression  mce)

             {

                 if  (((mce.Arguments.Count > 0) 

                     && (mce.Arguments[0].NodeType == ExpressionType .Constant)) 

                     && ((((ConstantExpression ) mce.Arguments[0]).Value != null ) 

                     && (((ConstantExpression ) mce.Arguments[0]).Value is  IQueryable )))

                 {

                     List <Expression > list = new  List <Expression > {

                         _root

                     };

                     list.AddRange(mce.Arguments.Skip<Expression >(1));

                     return  Expression .Call(mce.Method, list.ToArray());

                 }

                 return  base .VisitMethodCall(mce);

             }

         }

     }

 }

 


I metodi statici in testa alla classe credo che parlino da soli… :)


Ciao a tutti e a presto (spero)


Marco