I need to create a dynamic linq expression for a dynamic search.The basic search is working but it fails to work with collection. I am able to get the book\'s title and auth
You can use the method described here.
You would need to cast the result of the method to Expression
. T being your type.
I will provide an complete example when i get home.
Edit:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Collections;
using System.Reflection;
namespace ExpressionPredicateBuilder
{
public enum OperatorComparer
{
Contains,
StartsWith,
EndsWith,
Equals = ExpressionType.Equal,
GreaterThan = ExpressionType.GreaterThan,
GreaterThanOrEqual = ExpressionType.GreaterThanOrEqual,
LessThan = ExpressionType.LessThan,
LessThanOrEqual = ExpressionType.LessThan,
NotEqual = ExpressionType.NotEqual
}
public class ExpressionBuilder
{
public static Expression> BuildPredicate(object value, OperatorComparer comparer, params string[] properties)
{
var parameterExpression = Expression.Parameter(typeof(T), typeof(T).Name);
return (Expression>)BuildNavigationExpression(parameterExpression, comparer, value, properties);
}
private static Expression BuildNavigationExpression(Expression parameter, OperatorComparer comparer, object value, params string[] properties)
{
Expression resultExpression = null;
Expression childParameter, predicate;
Type childType = null;
if (properties.Count() > 1)
{
//build path
parameter = Expression.Property(parameter, properties[0]);
var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type);
//if it´s a collection we later need to use the predicate in the methodexpressioncall
if (isCollection)
{
childType = parameter.Type.GetGenericArguments()[0];
childParameter = Expression.Parameter(childType, childType.Name);
}
else
{
childParameter = parameter;
}
//skip current property and get navigation property expression recursivly
var innerProperties = properties.Skip(1).ToArray();
predicate = BuildNavigationExpression(childParameter, comparer, value, innerProperties);
if (isCollection)
{
//build subquery
resultExpression = BuildSubQuery(parameter, childType, predicate);
}
else
{
resultExpression = predicate;
}
}
else
{
//build final predicate
resultExpression = BuildCondition(parameter, properties[0], comparer, value);
}
return resultExpression;
}
private static Expression BuildSubQuery(Expression parameter, Type childType, Expression predicate)
{
var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2);
anyMethod = anyMethod.MakeGenericMethod(childType);
predicate = Expression.Call(anyMethod, parameter, predicate);
return MakeLambda(parameter, predicate);
}
private static Expression BuildCondition(Expression parameter, string property, OperatorComparer comparer, object value)
{
var childProperty = parameter.Type.GetProperty(property);
var left = Expression.Property(parameter, childProperty);
var right = Expression.Constant(value);
var predicate = BuildComparsion(left, comparer, right);
return MakeLambda(parameter, predicate);
}
private static Expression BuildComparsion(Expression left, OperatorComparer comparer, Expression right)
{
var mask = new List{
OperatorComparer.Contains,
OperatorComparer.StartsWith,
OperatorComparer.EndsWith
};
if(mask.Contains(comparer) && left.Type != typeof(string))
{
comparer = OperatorComparer.Equals;
}
if(!mask.Contains(comparer))
{
return Expression.MakeBinary((ExpressionType)comparer, left, Expression.Convert(right,left.Type));
}
return BuildStringCondition(left, comparer, right);
}
private static Expression BuildStringCondition(Expression left, OperatorComparer comparer, Expression right)
{
var compareMethod = typeof(string).GetMethods().Single(m => m.Name.Equals(Enum.GetName(typeof(OperatorComparer), comparer)) && m.GetParameters().Count() == 1);
//we assume ignoreCase, so call ToLower on paramter and memberexpression
var toLowerMethod = typeof(string).GetMethods().Single(m => m.Name.Equals("ToLower") && m.GetParameters().Count() == 0);
left = Expression.Call(left, toLowerMethod);
right = Expression.Call(right, toLowerMethod);
return Expression.Call(left, compareMethod, right);
}
private static Expression MakeLambda(Expression parameter, Expression predicate)
{
var resultParameterVisitor = new ParameterVisitor();
resultParameterVisitor.Visit(parameter);
var resultParameter = resultParameterVisitor.Parameter;
return Expression.Lambda(predicate, (ParameterExpression)resultParameter);
}
private class ParameterVisitor : ExpressionVisitor
{
public Expression Parameter
{
get;
private set;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Parameter = node;
return node;
}
}
}
}
This can be used like
var predicate = ExpressionBuilder.BuildPredicate("Heading",OperatorComparer.Equals,"Page","Heading");
query = query.Where(predicate);