none
Building dynamic LINQ queries with ORed/ANDed expressions

    Question

  • I have a particular issue with LINQ syntax for mixed ORing/ANDing of query expressions, but will provide some background info before describing the actual issue in more detail.

    First, I received some good suggestions from some other Silverlight Forum members regarding Dynamic Link Queries (http://social.msdn.microsoft.com/Forums/en-US/silverlightarchieve/thread/f99ac700-b8cb-43d0-a58f-dfbe24f00526//1?LINQ+Dynamic+Query+Library). I really appreciate the willingness of others on the forum to lend a helping hand...

    One person suggested an approach leveraged from a Scott Gu blog on this subject. The approach consists of generating SQL string expressions that are then easily uploaded via RIA services. This solution uses a module name Dynamics.cs, which is installed in the Silverlight project. After a little research/experimenting, I have been able to create all the functionality that I need for my application. However, I have not been able to figure out how to generate complex dynamic client-side queries using LINQ syntax in lieu of text.

    I have no problem generating and combining expressions as long as they are ANDed. However, I am still unable to achieve the desired results for a LINQ syntax query that implements arbitrary combinations of ORed/ANDed expressions like the following.

    1.Accept up to 30 arbitrary string keywords from a user control.

    2.Search a text database field for the desired keywords to determine if the keywords are contained in the data set.

    3.The user appends a symbol (e.g., &) to indicate that the keyword is to be ANDed with any other keywords that also have the appended symbol.

    4.Any keyword that the user does not append the symbol to is to be grouped into an ORed set.

    The result might look like this: foo& bar mar jar& toast most post& …

    To simplify the process for the text solution, I sort the data into two sets and then process each set in for loops to create groups similar to the following.

    (ItemField.Contains(“WordA1”) and … ItemField.Contains(“WordAN”)) and

    (ItemField.Contains(“WordO1”) or … ItemField.Contains(“WordON”))

    The above groups are then combined with other search criteria that comes from various user selected pulldown lists.

    The text approach works out to be quite simple using StingBuilder methods and the appropriate for loops. However, I am stuck on the LINQ syntax. I have not been able to figure out how to construct a loop that will enable me to generate the correct syntax for a group of dynamically created ORed expressions.

    One LINQ suggestion that I received was to use LINQPad and some of their utilities. However, I found that their PredicateBuilder does not seem to work with Silverlight 4/LINQ to Entities, which is an application constraint. I also found a Forum note on this subject. I think it was Colin Blair’s comment. I contacted LINQPad’s developer about Silverlight support. I was told that he did not really know much about Silverlight/RIA services.

    I have spent a lot of time searching all my books/ebooks, blogs, and forums. The only thing that I am sure of is that there are a lot of others struggling with this same issue.

    I am hoping that some kind LINQ guru will give me a hand with how to solve this type of client-side query. I am sure many others will benefit as well.

    Fortunately, I do have a reasonable solution that works. However, I can see some of the advantages of using a pure LINQ approach, but I just cannot figure out how to write the code.

    If you got this far, thank you for taking the time to read my request.

    Tuesday, January 10, 2012 12:43 PM

Answers

  • Just found a PredicateBuilder that is supposed to work in Silverlight. I haven't tried it myself so no promises.

     http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/

    Tuesday, January 10, 2012 1:47 PM
  • Given the example you outlined above, can you not simply use an ICollectionView to create the initial query (with filters etc. from your various drop downs) and then manually append the final where expression for your string filtering?

    It is not "too" difficult to construct a dynamic where expression
    You just create a Func<T,Bool> then call the EntityQuerey .Where(..) extension method on it
    Given that (AFAIK) your just filtering if one particular field contains the given string, it should be relatively easy to do it.

    (This is bits and pieces of chopped up code from my ICollectionView->EntityQuery lib)

    ParameterExpression parameterExpression = Expression.Parameter(currentType);
    
    
    //do the following for each of your properties
    
    Expression left = parameterExpression;
    
    currentType.GetProperty(propertyPaths[i]);//I had nested properties, so I had looped this
    left = Expression.Property(left, property);
    
    Expression right = Expression.Constant(filterDescriptor.Value.ToString());
    
    string methodName = "Contains";
    MethodInfo method = typeof(string).GetMethod(methodName, new Type[1] { typeof(string) });
    expression = Expression.Call(left, method, right);
    
    //Then Expression.AndAlso/OrElse (i.e. && ||) them together
    
    //Finally Lambda your expression
    Expression<Func<T, bool>> myExpression = Expression.Lambda<Func<T, bool>>(expression, parameterExpression);
    Tuesday, January 10, 2012 7:11 PM
  • Colin,

    Thanks again for your help.

    I was also able to create a dll from Pete Montgomery's PredicateBuilder and other referenced code links that can be used in Albahari's LINQPad, which is very convenient. I will include the PredicateBuilder code in a separate post in this topic. The code contains references to the respective authors and links to their separate code blocks/articles. Great kudos to Pete Montgomery, Colin Meek, and Matt Warren.

    I am using WCF RIA services and LINQ to Entities in Silverlight 4 and haven't found any issues. The approach significantly reduced my code as compared to using Dynamic.cs with text and the LINQ generated code is almost the same as SQL text generated code from my original approach. It makes for very compact for loops to generate the more complex scenarios that I described in my first post above. It is also more intuitive.

    I used the following process to add the dll to LINQPad and verify basic expression Oring functionality.

    1. Added dll namespace PB by pressing F4 in LINQPad and selecting Additional Namespace Imports tab. The dll was added similarly, but I selected the Additional References and browsed to the source.

    2. The added PB.dll contains a tweaked version of public static class PredicateBuilder. It also contains public class ParameterRebinder : ExpressionVisitor and public abstract class ExpressionVisitor.

    3. I wrote two lambda expressions for the Items table ItemTitle field, then Ored the two expressions. The Ored expression was used to query my MySQL database.

    4. The localQuery.Dump() results contained the correctly identified rows (10) from the table.

    Expression<Func> result1 = r1 => r1.ItemTitle.Contains(“oil”);

    Expression<Func> result2 = r2 => r2.ItemTitle.Contains(“painting”);

    Expression<Func> result;

    result = PB.PredicateBuilder.Or(result1, result2);

    IQueryable localQuery = from i in Items.Where(result) select i;

    localQuery.Dump();

    Monday, January 16, 2012 7:48 PM
  • /// <summary>
    /// Pete Montgomery's PredicateBuilder as referenced in article titled: A universal PredicateBuilderl
    /// Pete Montgomery article link: http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/ 
    /// Matt Warren’s ExpressionVisitor as referenced in Colin Meek's article titled: LINQ to Entities: Combining Predicates.
    /// Colin Meek article link: http://blogs.msdn.com/b/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx 
    /// Matt Warren's article is titled: LINQ: Building an IQueryable Provider - Part II
    /// Matt Warren article link: http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx 
    /// </summary>
    
    
    /// <summary>
    /// Enables the efficient, dynamic composition of query predicates.
    /// </summary>
    public static class PredicateBuilder
    {
    	/// <summary>
    	/// Creates a predicate that evaluates to true.
    	/// </summary>
    	public static Expression<Func<T, bool>> True<T>() { return param => true; }
    
    	/// <summary>
    	/// Creates a predicate that evaluates to false.
    	/// </summary>
    	public static Expression<Func<T, bool>> False<T>() { return param => false; }
    
    	/// <summary>
    	/// Creates a predicate expression from the specified lambda expression.
    	/// </summary>
    	public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }
    
    	/// <summary>
    	/// Combines the first predicate with the second using the logical "and".
    	/// </summary>
    	public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    	{
    		return first.Compose(second, Expression.AndAlso);
    	}
    
    	/// <summary>
    	/// Combines the first predicate with the second using the logical "or".
    	/// </summary>
    	public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    	{
    		return first.Compose(second, Expression.OrElse);
    	}
    
    	/// <summary>
    	/// Negates the predicate.
    	/// </summary>
    	public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
    	{
    		var negated = Expression.Not(expression.Body);
    		return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
    	}
    
    	/// <summary>
    	/// Combines the first expression with the second using the specified merge function.
    	/// </summary>
    	static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
    	{
    		// zip parameters (map from parameters of second to parameters of first)
    		var map = first.Parameters
    			.Select((f, i) => new { f, s = second.Parameters[i] })
    			.ToDictionary(p => p.s, p => p.f);
    
    		// replace parameters in the second lambda expression with the parameters in the first
    		var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
    
    		// create a merged lambda expression with parameters from the first expression
    		return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
    	}
    }
    
    public class ParameterRebinder : ExpressionVisitor
    {
    	private readonly Dictionary<ParameterExpression, ParameterExpression> map;
    	public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
    	{
    		this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
    	}
    	public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
    	{
    		return new ParameterRebinder(map).Visit(exp);
    	}
    	protected override Expression VisitParameter(ParameterExpression p)
    	{
    		ParameterExpression replacement;
    		if (map.TryGetValue(p, out replacement))
    		{
    			p = replacement;
    		}
    		return base.VisitParameter(p);
    	}
    }
    
    /// <summary>
    /// Matt Warren’s ExpressionVisitor as referenced in Colin Meek's article titled: LINQ to Entities: Combining Predicates.
    /// Colin Meek article link: http://blogs.msdn.com/b/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx
    /// Matt Warren's article is titled: LINQ: Building an IQueryable Provider - Part II
    /// Matt Warren article link: http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx
    /// </summary>
    public abstract class ExpressionVisitor
    {
    	protected ExpressionVisitor()
    	{
    	}
    	protected virtual Expression Visit(Expression exp)
    	{
    		if (exp == null)
    			return exp;
    		switch (exp.NodeType)
    		{
    			case ExpressionType.Negate:
    			case ExpressionType.NegateChecked:
    			case ExpressionType.Not:
    			case ExpressionType.Convert:
    			case ExpressionType.ConvertChecked:
    			case ExpressionType.ArrayLength:
    			case ExpressionType.Quote:
    			case ExpressionType.TypeAs:
    				return this.VisitUnary((UnaryExpression)exp);
    			case ExpressionType.Add:
    			case ExpressionType.AddChecked:
    			case ExpressionType.Subtract:
    			case ExpressionType.SubtractChecked:
    			case ExpressionType.Multiply:
    			case ExpressionType.MultiplyChecked:
    			case ExpressionType.Divide:
    			case ExpressionType.Modulo:
    			case ExpressionType.And:
    			case ExpressionType.AndAlso:
    			case ExpressionType.Or:
    			case ExpressionType.OrElse:
    			case ExpressionType.LessThan:
    			case ExpressionType.LessThanOrEqual:
    			case ExpressionType.GreaterThan:
    			case ExpressionType.GreaterThanOrEqual:
    			case ExpressionType.Equal:
    			case ExpressionType.NotEqual:
    			case ExpressionType.Coalesce:
    			case ExpressionType.ArrayIndex:
    			case ExpressionType.RightShift:
    			case ExpressionType.LeftShift:
    			case ExpressionType.ExclusiveOr:
    				return this.VisitBinary((BinaryExpression)exp);
    			case ExpressionType.TypeIs:
    				return this.VisitTypeIs((TypeBinaryExpression)exp);
    			case ExpressionType.Conditional:
    				return this.VisitConditional((ConditionalExpression)exp);
    			case ExpressionType.Constant:
    				return this.VisitConstant((ConstantExpression)exp);
    			case ExpressionType.Parameter:
    				return this.VisitParameter((ParameterExpression)exp);
    			case ExpressionType.MemberAccess:
    				return this.VisitMemberAccess((MemberExpression)exp);
    			case ExpressionType.Call:
    				return this.VisitMethodCall((MethodCallExpression)exp);
    			case ExpressionType.Lambda:
    				return this.VisitLambda((LambdaExpression)exp);
    			case ExpressionType.New:
    				return this.VisitNew((NewExpression)exp);
    			case ExpressionType.NewArrayInit:
    			case ExpressionType.NewArrayBounds:
    				return this.VisitNewArray((NewArrayExpression)exp);
    			case ExpressionType.Invoke:
    				return this.VisitInvocation((InvocationExpression)exp);
    			case ExpressionType.MemberInit:
    				return this.VisitMemberInit((MemberInitExpression)exp);
    			case ExpressionType.ListInit:
    				return this.VisitListInit((ListInitExpression)exp);
    			default:
    				throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType));
    		}
    	}
    	protected virtual MemberBinding VisitBinding(MemberBinding binding)
    	{
    		switch (binding.BindingType)
    		{
    			case MemberBindingType.Assignment:
    				return this.VisitMemberAssignment((MemberAssignment)binding);
    			case MemberBindingType.MemberBinding:
    				return this.VisitMemberMemberBinding((MemberMemberBinding)binding);
    			case MemberBindingType.ListBinding:
    				return this.VisitMemberListBinding((MemberListBinding)binding);
    			default:
    				throw new Exception(string.Format("Unhandled binding type '{0}'", binding.BindingType));
    		}
    	}
    	protected virtual ElementInit VisitElementInitializer(ElementInit initializer)
    	{
    		ReadOnlyCollection<Expression> arguments = this.VisitExpressionList(initializer.Arguments);
    		if (arguments != initializer.Arguments)
    		{
    			return Expression.ElementInit(initializer.AddMethod, arguments);
    		}
    		return initializer;
    	}
    	protected virtual Expression VisitUnary(UnaryExpression u)
    	{
    		Expression operand = this.Visit(u.Operand);
    		if (operand != u.Operand)
    		{
    			return Expression.MakeUnary(u.NodeType, operand, u.Type, u.Method);
    		}
    		return u;
    	}
    	protected virtual Expression VisitBinary(BinaryExpression b)
    	{
    		Expression left = this.Visit(b.Left);
    		Expression right = this.Visit(b.Right);
    		Expression conversion = this.Visit(b.Conversion);
    		if (left != b.Left || right != b.Right || conversion != b.Conversion)
    		{
    			if (b.NodeType == ExpressionType.Coalesce && b.Conversion != null)
    				return Expression.Coalesce(left, right, conversion as LambdaExpression);
    			else
    				return Expression.MakeBinary(b.NodeType, left, right, b.IsLiftedToNull, b.Method);
    		}
    		return b;
    	}
    	protected virtual Expression VisitTypeIs(TypeBinaryExpression b)
    	{
    		Expression expr = this.Visit(b.Expression);
    		if (expr != b.Expression)
    		{
    			return Expression.TypeIs(expr, b.TypeOperand);
    		}
    		return b;
    	}
    	protected virtual Expression VisitConstant(ConstantExpression c)
    	{
    		return c;
    	}
    	protected virtual Expression VisitConditional(ConditionalExpression c)
    	{
    		Expression test = this.Visit(c.Test);
    		Expression ifTrue = this.Visit(c.IfTrue);
    		Expression ifFalse = this.Visit(c.IfFalse);
    		if (test != c.Test || ifTrue != c.IfTrue || ifFalse != c.IfFalse)
    		{
    			return Expression.Condition(test, ifTrue, ifFalse);
    		}
    		return c;
    	}
    	protected virtual Expression VisitParameter(ParameterExpression p)
    	{
    		return p;
    	}
    	protected virtual Expression VisitMemberAccess(MemberExpression m)
    	{
    		Expression exp = this.Visit(m.Expression);
    		if (exp != m.Expression)
    		{
    			return Expression.MakeMemberAccess(exp, m.Member);
    		}
    		return m;
    	}
    	protected virtual Expression VisitMethodCall(MethodCallExpression m)
    	{
    		Expression obj = this.Visit(m.Object);
    		IEnumerable<Expression> args = this.VisitExpressionList(m.Arguments);
    		if (obj != m.Object || args != m.Arguments)
    		{
    			return Expression.Call(obj, m.Method, args);
    		}
    		return m;
    	}
    	protected virtual ReadOnlyCollection<Expression> VisitExpressionList(ReadOnlyCollection<Expression> original)
    	{
    		List<Expression> list = null;
    		for (int i = 0, n = original.Count; i < n; i++)
    		{
    			Expression p = this.Visit(original[i]);
    			if (list != null)
    			{
    				list.Add(p);
    			}
    			else if (p != original[i])
    			{
    				list = new List<Expression>(n);
    				for (int j = 0; j < i; j++)
    				{
    					list.Add(original[j]);
    				}
    				list.Add(p);
    			}
    		}
    		if (list != null)
    		{
    			return list.AsReadOnly();
    		}
    		return original;
    	}
    	protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment assignment)
    	{
    		Expression e = this.Visit(assignment.Expression);
    		if (e != assignment.Expression)
    		{
    			return Expression.Bind(assignment.Member, e);
    		}
    		return assignment;
    	}
    	protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding)
    	{
    		IEnumerable<MemberBinding> bindings = this.VisitBindingList(binding.Bindings);
    		if (bindings != binding.Bindings)
    		{
    			return Expression.MemberBind(binding.Member, bindings);
    		}
    		return binding;
    	}
    	protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding)
    	{
    		IEnumerable<ElementInit> initializers = this.VisitElementInitializerList(binding.Initializers);
    		if (initializers != binding.Initializers)
    		{
    			return Expression.ListBind(binding.Member, initializers);
    		}
    		return binding;
    	}
    	protected virtual IEnumerable<MemberBinding> VisitBindingList(ReadOnlyCollection<MemberBinding> original)
    	{
    		List<MemberBinding> list = null;
    		for (int i = 0, n = original.Count; i < n; i++)
    		{
    			MemberBinding b = this.VisitBinding(original[i]);
    			if (list != null)
    			{
    				list.Add(b);
    			}
    			else if (b != original[i])
    			{
    				list = new List<MemberBinding>(n);
    				for (int j = 0; j < i; j++)
    				{
    					list.Add(original[j]);
    				}
    				list.Add(b);
    			}
    		}
    		if (list != null)
    			return list;
    		return original;
    	}
    	protected virtual IEnumerable<ElementInit> VisitElementInitializerList(ReadOnlyCollection<ElementInit> original)
    	{
    		List<ElementInit> list = null;
    		for (int i = 0, n = original.Count; i < n; i++)
    		{
    			ElementInit init = this.VisitElementInitializer(original[i]);
    			if (list != null)
    			{
    				list.Add(init);
    			}
    			else if (init != original[i])
    			{
    				list = new List<ElementInit>(n);
    				for (int j = 0; j < i; j++)
    				{
    					list.Add(original[j]);
    				}
    				list.Add(init);
    			}
    		}
    		if (list != null)
    			return list;
    		return original;
    	}
    	protected virtual Expression VisitLambda(LambdaExpression lambda)
    	{
    		Expression body = this.Visit(lambda.Body);
    		if (body != lambda.Body)
    		{
    			return Expression.Lambda(lambda.Type, body, lambda.Parameters);
    		}
    		return lambda;
    	}
    	protected virtual NewExpression VisitNew(NewExpression nex)
    	{
    		IEnumerable<Expression> args = this.VisitExpressionList(nex.Arguments);
    		if (args != nex.Arguments)
    		{
    			if (nex.Members != null)
    				return Expression.New(nex.Constructor, args, nex.Members);
    			else
    				return Expression.New(nex.Constructor, args);
    		}
    		return nex;
    	}
    	protected virtual Expression VisitMemberInit(MemberInitExpression init)
    	{
    		NewExpression n = this.VisitNew(init.NewExpression);
    		IEnumerable<MemberBinding> bindings = this.VisitBindingList(init.Bindings);
    		if (n != init.NewExpression || bindings != init.Bindings)
    		{
    			return Expression.MemberInit(n, bindings);
    		}
    		return init;
    	}
    	protected virtual Expression VisitListInit(ListInitExpression init)
    	{
    		NewExpression n = this.VisitNew(init.NewExpression);
    		IEnumerable<ElementInit> initializers = this.VisitElementInitializerList(init.Initializers);
    		if (n != init.NewExpression || initializers != init.Initializers)
    		{
    			return Expression.ListInit(n, initializers);
    		}
    		return init;
    	}
    	protected virtual Expression VisitNewArray(NewArrayExpression na)
    	{
    		IEnumerable<Expression> exprs = this.VisitExpressionList(na.Expressions);
    		if (exprs != na.Expressions)
    		{
    			if (na.NodeType == ExpressionType.NewArrayInit)
    			{
    				return Expression.NewArrayInit(na.Type.GetElementType(), exprs);
    			}
    			else
    			{
    				return Expression.NewArrayBounds(na.Type.GetElementType(), exprs);
    			}
    		}
    		return na;
    	}
    	protected virtual Expression VisitInvocation(InvocationExpression iv)
    	{
    		IEnumerable<Expression> args = this.VisitExpressionList(iv.Arguments);
    		Expression expr = this.Visit(iv.Expression);
    		if (args != iv.Arguments || expr != iv.Expression)
    		{
    			return Expression.Invoke(expr, args);
    		}
    		return iv;
    	}
    }
    
    
    Monday, January 16, 2012 7:53 PM

All replies

  • PredicateBuilder uses private reflection which isn't possible in Silverlight. That leaves the dynamic library as the best way to do what you are trying to do which is why I usually recommend it.

    Tuesday, January 10, 2012 1:40 PM
  • Just found a PredicateBuilder that is supposed to work in Silverlight. I haven't tried it myself so no promises.

     http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/

    Tuesday, January 10, 2012 1:47 PM
  • Given the example you outlined above, can you not simply use an ICollectionView to create the initial query (with filters etc. from your various drop downs) and then manually append the final where expression for your string filtering?

    It is not "too" difficult to construct a dynamic where expression
    You just create a Func<T,Bool> then call the EntityQuerey .Where(..) extension method on it
    Given that (AFAIK) your just filtering if one particular field contains the given string, it should be relatively easy to do it.

    (This is bits and pieces of chopped up code from my ICollectionView->EntityQuery lib)

    ParameterExpression parameterExpression = Expression.Parameter(currentType);
    
    
    //do the following for each of your properties
    
    Expression left = parameterExpression;
    
    currentType.GetProperty(propertyPaths[i]);//I had nested properties, so I had looped this
    left = Expression.Property(left, property);
    
    Expression right = Expression.Constant(filterDescriptor.Value.ToString());
    
    string methodName = "Contains";
    MethodInfo method = typeof(string).GetMethod(methodName, new Type[1] { typeof(string) });
    expression = Expression.Call(left, method, right);
    
    //Then Expression.AndAlso/OrElse (i.e. && ||) them together
    
    //Finally Lambda your expression
    Expression<Func<T, bool>> myExpression = Expression.Lambda<Func<T, bool>>(expression, parameterExpression);
    Tuesday, January 10, 2012 7:11 PM
  • Colin,

    Thanks much for the link. There is at least one person on the blog that reported that it worked with Silverlight 4. I'll give it a try today and update this post later.

    Thursday, January 12, 2012 5:08 AM
  • s9ilent,

    Thank you for your suggestion. I'll attempt to digest/try your approach and post back my results.

    Thursday, January 12, 2012 5:10 AM
  • Colin,

    I have had some time to look at Pete Montgomery's alternate PredicateBuilder. I have been able to perform the type of dynamic queries outlined in my original post. I will be documenting more on this topic in a subsequent addition to this post.

    Thanks for your assistance.

    Warren

    Monday, January 16, 2012 6:16 AM
  • S9ilent,

    It took me a while to digest your input. With a little help from a few web posts and an article by Colin Meek at http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx,

    I was able to accomplish the goals set forth in this post using your approach.

    I have decided to go with Pete Montgomery's approach, because it offers the most compact and flexible approach that I have found to date. Thanks again for your input.

    Monday, January 16, 2012 6:26 AM
  • Colin,

    Thanks again for your help.

    I was also able to create a dll from Pete Montgomery's PredicateBuilder and other referenced code links that can be used in Albahari's LINQPad, which is very convenient. I will include the PredicateBuilder code in a separate post in this topic. The code contains references to the respective authors and links to their separate code blocks/articles. Great kudos to Pete Montgomery, Colin Meek, and Matt Warren.

    I am using WCF RIA services and LINQ to Entities in Silverlight 4 and haven't found any issues. The approach significantly reduced my code as compared to using Dynamic.cs with text and the LINQ generated code is almost the same as SQL text generated code from my original approach. It makes for very compact for loops to generate the more complex scenarios that I described in my first post above. It is also more intuitive.

    I used the following process to add the dll to LINQPad and verify basic expression Oring functionality.

    1. Added dll namespace PB by pressing F4 in LINQPad and selecting Additional Namespace Imports tab. The dll was added similarly, but I selected the Additional References and browsed to the source.

    2. The added PB.dll contains a tweaked version of public static class PredicateBuilder. It also contains public class ParameterRebinder : ExpressionVisitor and public abstract class ExpressionVisitor.

    3. I wrote two lambda expressions for the Items table ItemTitle field, then Ored the two expressions. The Ored expression was used to query my MySQL database.

    4. The localQuery.Dump() results contained the correctly identified rows (10) from the table.

    Expression<Func> result1 = r1 => r1.ItemTitle.Contains(“oil”);

    Expression<Func> result2 = r2 => r2.ItemTitle.Contains(“painting”);

    Expression<Func> result;

    result = PB.PredicateBuilder.Or(result1, result2);

    IQueryable localQuery = from i in Items.Where(result) select i;

    localQuery.Dump();

    Monday, January 16, 2012 7:48 PM
  • /// <summary>
    /// Pete Montgomery's PredicateBuilder as referenced in article titled: A universal PredicateBuilderl
    /// Pete Montgomery article link: http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/ 
    /// Matt Warren’s ExpressionVisitor as referenced in Colin Meek's article titled: LINQ to Entities: Combining Predicates.
    /// Colin Meek article link: http://blogs.msdn.com/b/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx 
    /// Matt Warren's article is titled: LINQ: Building an IQueryable Provider - Part II
    /// Matt Warren article link: http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx 
    /// </summary>
    
    
    /// <summary>
    /// Enables the efficient, dynamic composition of query predicates.
    /// </summary>
    public static class PredicateBuilder
    {
    	/// <summary>
    	/// Creates a predicate that evaluates to true.
    	/// </summary>
    	public static Expression<Func<T, bool>> True<T>() { return param => true; }
    
    	/// <summary>
    	/// Creates a predicate that evaluates to false.
    	/// </summary>
    	public static Expression<Func<T, bool>> False<T>() { return param => false; }
    
    	/// <summary>
    	/// Creates a predicate expression from the specified lambda expression.
    	/// </summary>
    	public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }
    
    	/// <summary>
    	/// Combines the first predicate with the second using the logical "and".
    	/// </summary>
    	public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    	{
    		return first.Compose(second, Expression.AndAlso);
    	}
    
    	/// <summary>
    	/// Combines the first predicate with the second using the logical "or".
    	/// </summary>
    	public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    	{
    		return first.Compose(second, Expression.OrElse);
    	}
    
    	/// <summary>
    	/// Negates the predicate.
    	/// </summary>
    	public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
    	{
    		var negated = Expression.Not(expression.Body);
    		return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
    	}
    
    	/// <summary>
    	/// Combines the first expression with the second using the specified merge function.
    	/// </summary>
    	static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
    	{
    		// zip parameters (map from parameters of second to parameters of first)
    		var map = first.Parameters
    			.Select((f, i) => new { f, s = second.Parameters[i] })
    			.ToDictionary(p => p.s, p => p.f);
    
    		// replace parameters in the second lambda expression with the parameters in the first
    		var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
    
    		// create a merged lambda expression with parameters from the first expression
    		return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
    	}
    }
    
    public class ParameterRebinder : ExpressionVisitor
    {
    	private readonly Dictionary<ParameterExpression, ParameterExpression> map;
    	public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
    	{
    		this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
    	}
    	public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
    	{
    		return new ParameterRebinder(map).Visit(exp);
    	}
    	protected override Expression VisitParameter(ParameterExpression p)
    	{
    		ParameterExpression replacement;
    		if (map.TryGetValue(p, out replacement))
    		{
    			p = replacement;
    		}
    		return base.VisitParameter(p);
    	}
    }
    
    /// <summary>
    /// Matt Warren’s ExpressionVisitor as referenced in Colin Meek's article titled: LINQ to Entities: Combining Predicates.
    /// Colin Meek article link: http://blogs.msdn.com/b/mattwar/archive/2007/07/31/linq-building-an-iqueryable-provider-part-ii.aspx
    /// Matt Warren's article is titled: LINQ: Building an IQueryable Provider - Part II
    /// Matt Warren article link: http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx
    /// </summary>
    public abstract class ExpressionVisitor
    {
    	protected ExpressionVisitor()
    	{
    	}
    	protected virtual Expression Visit(Expression exp)
    	{
    		if (exp == null)
    			return exp;
    		switch (exp.NodeType)
    		{
    			case ExpressionType.Negate:
    			case ExpressionType.NegateChecked:
    			case ExpressionType.Not:
    			case ExpressionType.Convert:
    			case ExpressionType.ConvertChecked:
    			case ExpressionType.ArrayLength:
    			case ExpressionType.Quote:
    			case ExpressionType.TypeAs:
    				return this.VisitUnary((UnaryExpression)exp);
    			case ExpressionType.Add:
    			case ExpressionType.AddChecked:
    			case ExpressionType.Subtract:
    			case ExpressionType.SubtractChecked:
    			case ExpressionType.Multiply:
    			case ExpressionType.MultiplyChecked:
    			case ExpressionType.Divide:
    			case ExpressionType.Modulo:
    			case ExpressionType.And:
    			case ExpressionType.AndAlso:
    			case ExpressionType.Or:
    			case ExpressionType.OrElse:
    			case ExpressionType.LessThan:
    			case ExpressionType.LessThanOrEqual:
    			case ExpressionType.GreaterThan:
    			case ExpressionType.GreaterThanOrEqual:
    			case ExpressionType.Equal:
    			case ExpressionType.NotEqual:
    			case ExpressionType.Coalesce:
    			case ExpressionType.ArrayIndex:
    			case ExpressionType.RightShift:
    			case ExpressionType.LeftShift:
    			case ExpressionType.ExclusiveOr:
    				return this.VisitBinary((BinaryExpression)exp);
    			case ExpressionType.TypeIs:
    				return this.VisitTypeIs((TypeBinaryExpression)exp);
    			case ExpressionType.Conditional:
    				return this.VisitConditional((ConditionalExpression)exp);
    			case ExpressionType.Constant:
    				return this.VisitConstant((ConstantExpression)exp);
    			case ExpressionType.Parameter:
    				return this.VisitParameter((ParameterExpression)exp);
    			case ExpressionType.MemberAccess:
    				return this.VisitMemberAccess((MemberExpression)exp);
    			case ExpressionType.Call:
    				return this.VisitMethodCall((MethodCallExpression)exp);
    			case ExpressionType.Lambda:
    				return this.VisitLambda((LambdaExpression)exp);
    			case ExpressionType.New:
    				return this.VisitNew((NewExpression)exp);
    			case ExpressionType.NewArrayInit:
    			case ExpressionType.NewArrayBounds:
    				return this.VisitNewArray((NewArrayExpression)exp);
    			case ExpressionType.Invoke:
    				return this.VisitInvocation((InvocationExpression)exp);
    			case ExpressionType.MemberInit:
    				return this.VisitMemberInit((MemberInitExpression)exp);
    			case ExpressionType.ListInit:
    				return this.VisitListInit((ListInitExpression)exp);
    			default:
    				throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType));
    		}
    	}
    	protected virtual MemberBinding VisitBinding(MemberBinding binding)
    	{
    		switch (binding.BindingType)
    		{
    			case MemberBindingType.Assignment:
    				return this.VisitMemberAssignment((MemberAssignment)binding);
    			case MemberBindingType.MemberBinding:
    				return this.VisitMemberMemberBinding((MemberMemberBinding)binding);
    			case MemberBindingType.ListBinding:
    				return this.VisitMemberListBinding((MemberListBinding)binding);
    			default:
    				throw new Exception(string.Format("Unhandled binding type '{0}'", binding.BindingType));
    		}
    	}
    	protected virtual ElementInit VisitElementInitializer(ElementInit initializer)
    	{
    		ReadOnlyCollection<Expression> arguments = this.VisitExpressionList(initializer.Arguments);
    		if (arguments != initializer.Arguments)
    		{
    			return Expression.ElementInit(initializer.AddMethod, arguments);
    		}
    		return initializer;
    	}
    	protected virtual Expression VisitUnary(UnaryExpression u)
    	{
    		Expression operand = this.Visit(u.Operand);
    		if (operand != u.Operand)
    		{
    			return Expression.MakeUnary(u.NodeType, operand, u.Type, u.Method);
    		}
    		return u;
    	}
    	protected virtual Expression VisitBinary(BinaryExpression b)
    	{
    		Expression left = this.Visit(b.Left);
    		Expression right = this.Visit(b.Right);
    		Expression conversion = this.Visit(b.Conversion);
    		if (left != b.Left || right != b.Right || conversion != b.Conversion)
    		{
    			if (b.NodeType == ExpressionType.Coalesce && b.Conversion != null)
    				return Expression.Coalesce(left, right, conversion as LambdaExpression);
    			else
    				return Expression.MakeBinary(b.NodeType, left, right, b.IsLiftedToNull, b.Method);
    		}
    		return b;
    	}
    	protected virtual Expression VisitTypeIs(TypeBinaryExpression b)
    	{
    		Expression expr = this.Visit(b.Expression);
    		if (expr != b.Expression)
    		{
    			return Expression.TypeIs(expr, b.TypeOperand);
    		}
    		return b;
    	}
    	protected virtual Expression VisitConstant(ConstantExpression c)
    	{
    		return c;
    	}
    	protected virtual Expression VisitConditional(ConditionalExpression c)
    	{
    		Expression test = this.Visit(c.Test);
    		Expression ifTrue = this.Visit(c.IfTrue);
    		Expression ifFalse = this.Visit(c.IfFalse);
    		if (test != c.Test || ifTrue != c.IfTrue || ifFalse != c.IfFalse)
    		{
    			return Expression.Condition(test, ifTrue, ifFalse);
    		}
    		return c;
    	}
    	protected virtual Expression VisitParameter(ParameterExpression p)
    	{
    		return p;
    	}
    	protected virtual Expression VisitMemberAccess(MemberExpression m)
    	{
    		Expression exp = this.Visit(m.Expression);
    		if (exp != m.Expression)
    		{
    			return Expression.MakeMemberAccess(exp, m.Member);
    		}
    		return m;
    	}
    	protected virtual Expression VisitMethodCall(MethodCallExpression m)
    	{
    		Expression obj = this.Visit(m.Object);
    		IEnumerable<Expression> args = this.VisitExpressionList(m.Arguments);
    		if (obj != m.Object || args != m.Arguments)
    		{
    			return Expression.Call(obj, m.Method, args);
    		}
    		return m;
    	}
    	protected virtual ReadOnlyCollection<Expression> VisitExpressionList(ReadOnlyCollection<Expression> original)
    	{
    		List<Expression> list = null;
    		for (int i = 0, n = original.Count; i < n; i++)
    		{
    			Expression p = this.Visit(original[i]);
    			if (list != null)
    			{
    				list.Add(p);
    			}
    			else if (p != original[i])
    			{
    				list = new List<Expression>(n);
    				for (int j = 0; j < i; j++)
    				{
    					list.Add(original[j]);
    				}
    				list.Add(p);
    			}
    		}
    		if (list != null)
    		{
    			return list.AsReadOnly();
    		}
    		return original;
    	}
    	protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment assignment)
    	{
    		Expression e = this.Visit(assignment.Expression);
    		if (e != assignment.Expression)
    		{
    			return Expression.Bind(assignment.Member, e);
    		}
    		return assignment;
    	}
    	protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding)
    	{
    		IEnumerable<MemberBinding> bindings = this.VisitBindingList(binding.Bindings);
    		if (bindings != binding.Bindings)
    		{
    			return Expression.MemberBind(binding.Member, bindings);
    		}
    		return binding;
    	}
    	protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding)
    	{
    		IEnumerable<ElementInit> initializers = this.VisitElementInitializerList(binding.Initializers);
    		if (initializers != binding.Initializers)
    		{
    			return Expression.ListBind(binding.Member, initializers);
    		}
    		return binding;
    	}
    	protected virtual IEnumerable<MemberBinding> VisitBindingList(ReadOnlyCollection<MemberBinding> original)
    	{
    		List<MemberBinding> list = null;
    		for (int i = 0, n = original.Count; i < n; i++)
    		{
    			MemberBinding b = this.VisitBinding(original[i]);
    			if (list != null)
    			{
    				list.Add(b);
    			}
    			else if (b != original[i])
    			{
    				list = new List<MemberBinding>(n);
    				for (int j = 0; j < i; j++)
    				{
    					list.Add(original[j]);
    				}
    				list.Add(b);
    			}
    		}
    		if (list != null)
    			return list;
    		return original;
    	}
    	protected virtual IEnumerable<ElementInit> VisitElementInitializerList(ReadOnlyCollection<ElementInit> original)
    	{
    		List<ElementInit> list = null;
    		for (int i = 0, n = original.Count; i < n; i++)
    		{
    			ElementInit init = this.VisitElementInitializer(original[i]);
    			if (list != null)
    			{
    				list.Add(init);
    			}
    			else if (init != original[i])
    			{
    				list = new List<ElementInit>(n);
    				for (int j = 0; j < i; j++)
    				{
    					list.Add(original[j]);
    				}
    				list.Add(init);
    			}
    		}
    		if (list != null)
    			return list;
    		return original;
    	}
    	protected virtual Expression VisitLambda(LambdaExpression lambda)
    	{
    		Expression body = this.Visit(lambda.Body);
    		if (body != lambda.Body)
    		{
    			return Expression.Lambda(lambda.Type, body, lambda.Parameters);
    		}
    		return lambda;
    	}
    	protected virtual NewExpression VisitNew(NewExpression nex)
    	{
    		IEnumerable<Expression> args = this.VisitExpressionList(nex.Arguments);
    		if (args != nex.Arguments)
    		{
    			if (nex.Members != null)
    				return Expression.New(nex.Constructor, args, nex.Members);
    			else
    				return Expression.New(nex.Constructor, args);
    		}
    		return nex;
    	}
    	protected virtual Expression VisitMemberInit(MemberInitExpression init)
    	{
    		NewExpression n = this.VisitNew(init.NewExpression);
    		IEnumerable<MemberBinding> bindings = this.VisitBindingList(init.Bindings);
    		if (n != init.NewExpression || bindings != init.Bindings)
    		{
    			return Expression.MemberInit(n, bindings);
    		}
    		return init;
    	}
    	protected virtual Expression VisitListInit(ListInitExpression init)
    	{
    		NewExpression n = this.VisitNew(init.NewExpression);
    		IEnumerable<ElementInit> initializers = this.VisitElementInitializerList(init.Initializers);
    		if (n != init.NewExpression || initializers != init.Initializers)
    		{
    			return Expression.ListInit(n, initializers);
    		}
    		return init;
    	}
    	protected virtual Expression VisitNewArray(NewArrayExpression na)
    	{
    		IEnumerable<Expression> exprs = this.VisitExpressionList(na.Expressions);
    		if (exprs != na.Expressions)
    		{
    			if (na.NodeType == ExpressionType.NewArrayInit)
    			{
    				return Expression.NewArrayInit(na.Type.GetElementType(), exprs);
    			}
    			else
    			{
    				return Expression.NewArrayBounds(na.Type.GetElementType(), exprs);
    			}
    		}
    		return na;
    	}
    	protected virtual Expression VisitInvocation(InvocationExpression iv)
    	{
    		IEnumerable<Expression> args = this.VisitExpressionList(iv.Arguments);
    		Expression expr = this.Visit(iv.Expression);
    		if (args != iv.Arguments || expr != iv.Expression)
    		{
    			return Expression.Invoke(expr, args);
    		}
    		return iv;
    	}
    }
    
    
    Monday, January 16, 2012 7:53 PM