How to use to support the Contains, StartsWith and EndsWith operations with LINQ Expression
// sample expression to generate dynamically
p => (p.Id >= 2 && p.Id <= 4)
&& (p.Birth != null p.Birth.Country != null)
&& p.Contacts.Any(c => c.Value.EndsWith("@email.com")
|| (p.Name != null && p.Name.Trim().ToLower().Contains("john")))
// This is how the code is used
var filter = new Filter<Person>();
filter.By("Id", Operation.Between, 2, 4)
.And.By("Birth.Country", Operation.IsNotNull)
.And.By("Contacts[Value]", Operations.EndsWith, "@email.com")
.Or.By("Name", Operaions.Contains, " John");
static MethodInfo containsMethod = typeof(string).GetMethod("Contains");
static MethodInfo startsWithMethod = typeof(string)
.GetMethod("StartsWith", new [] { typeof(string) });
static MethodInfo endsWithMethod = typeof(string)
.GetMethod("EndsWith", new [] { typeof(string) });
//We need to add the Trim and ToLower MethodInfos
static MethodInfo trimMethod = typeof(string).GetMethod("Trim", new Type[0]);
static MethodInfo toLowerMethod = typeof(string).GetMethod("ToLower", new Type[0]);
public Expression<Func<TClass, bool>> BuildExpression()
{
//this is in case the list of statements is empty
Expression finalExpression = Expression.Constant(true);
var parameter = Expression.Parameter(typeof(TClass), "x");
var connector = FilterStatementConnector.And;
foreach (var statement in Statements)
{
var member = GetMemberExpression(parameter, statement.PropertyName);
var constant = Expression.Constant(statement.Value);
if (statement.Value is string)
{
// x.Name.Trim()
var trimMemberCall = Expression.Call(member, trimMethod);
// x.Name.Trim().ToLower()
member = Expression.Call(trimMemberCall, toLowerMethod);
// "John ".Trim()
var trimConstantCall = Expression.Call(constant, trimMethod);
// "John ".Trim().ToLower()
constant = Expression.Call(trimConstantCall, toLowerMethod);
}
var expression = Expressions[statement.Operation].Invoke(member, constant);
finalExpression = CombineExpressions(finalExpression, expression, connector);
connector = statement.Connector;
}
return finalExpression;
}
Expression CombineExpressions(Expression expr1,
Expression expr2, FilterStatementConnector connector)
{
return connector == FilterStatementConnector.And ?
Expression.AndAlso(expr1, expr2) : Expression.OrElse(expr1, expr2);
}
MemberExpression GetMemberExpression(Expression param, string propertyName)
{
if (propertyName.Contains("."))
{
int index = propertyName.IndexOf(".");
var subParam = Expression.Property(param, propertyName.Substring(0, index));
return GetMemberExpression(subParam, propertyName.Substring(index + 1));
}
return Expression.Property(param, propertyName);
}