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 itvar otherQuery = query.Skip(1).Take(2).OrderByDescending(x=> x.Id);//print out original countConsole .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
3 comments:
hi, new to the site, thanks.
why not...
Thought I would comment and say neat theme, did you make it for yourself? It's really awesome!
Post a Comment