using System.Collections.Generic;
using System.Linq.Expressions;
static void Main(string[] args)
#region these parts are ok
var searchConditions = new List<SearchCondition>();
searchConditions.Add(new SearchCondition("EmpNo", "string" ,"=","MA0001"));
searchConditions.Add(new SearchCondition("EmpNo", "string" ,"==","MP0001", "or"));
searchConditions.Add(new SearchCondition("DepNo", "string" ,"!=","QC01", "and"));
var searchPredicate = SearchExtensions.BuildSearchToExpression<Employee>(searchConditions);
System.Console.WriteLine("=, ==, !=");
System.Console.WriteLine(searchPredicate.ToString());
System.Console.WriteLine("");
searchConditions = new List<SearchCondition>();
searchConditions.Add(new SearchCondition("DepNo", "string" ,"in","QC", "and"));
searchConditions.Add(new SearchCondition("EmpName", "string" ,"like","AB", "or"));
searchConditions.Add(new SearchCondition("PosNo", "string" ,"%","MA01", "or"));
searchPredicate = SearchExtensions.BuildSearchToExpression<Employee>(searchConditions);
System.Console.WriteLine("in ,% ,like");
System.Console.WriteLine(searchPredicate.ToString());
System.Console.WriteLine("");
searchConditions = new List<SearchCondition>();
searchConditions.Add(new SearchCondition("DepNo", "string" ,"!in","QC", "and"));
searchConditions.Add(new SearchCondition("EmpName", "string" ,"!like","AB", "or"));
searchConditions.Add(new SearchCondition("PosNo", "string" ,"!%","MA01", "or"));
searchPredicate = SearchExtensions.BuildSearchToExpression<Employee>(searchConditions);
System.Console.WriteLine("!in ,!% ,not like");
System.Console.WriteLine(searchPredicate.ToString());
System.Console.WriteLine("");
searchConditions = new List<SearchCondition>();
searchConditions.Add(new SearchCondition("EmpName", "string" ,"^","王", "and"));
searchConditions.Add(new SearchCondition("EmpName", "string" ,"start","田", "or"));
searchConditions.Add(new SearchCondition("EmpName", "string" ,"!^","陳", "and"));
searchPredicate = SearchExtensions.BuildSearchToExpression<Employee>(searchConditions);
System.Console.WriteLine("^ ,start ,not start");
System.Console.WriteLine(searchPredicate.ToString());
System.Console.WriteLine("");
searchConditions = new List<SearchCondition>();
searchConditions.Add(new SearchCondition("EmpName", "string" ,"$","芬", "and"));
searchConditions.Add(new SearchCondition("EmpName", "string" ,"end","田", "or"));
searchPredicate = SearchExtensions.BuildSearchToExpression<Employee>(searchConditions);
System.Console.WriteLine("$ ,end");
System.Console.WriteLine(searchPredicate.ToString());
System.Console.WriteLine("");
searchConditions = new List<SearchCondition>();
searchConditions.Add(new SearchCondition("HireDate", "date" ,">=","2021-1-1", "and"));
searchConditions.Add(new SearchCondition("HireDate", "date" ,"<=","2021-7-31", "and"));
searchConditions.Add(new SearchCondition("Salary", "int" ,">=","20000", "and"));
searchConditions.Add(new SearchCondition("Salary", "int" ,"<=","40000", "and"));
searchPredicate = SearchExtensions.BuildSearchToExpression<Employee>(searchConditions);
System.Console.WriteLine(">= ,<=");
System.Console.WriteLine(searchPredicate.ToString());
System.Console.WriteLine("");
searchConditions = new List<SearchCondition>();
searchConditions.Add(new SearchCondition("EmpNo", "string" ,">=","MA0001", "and"));
searchConditions.Add(new SearchCondition("EmpNo", "string" ,"<=","PQ9999", "and"));
searchConditions.Add(new SearchCondition("DepNo", "string" ,">=","QA01", "and"));
searchConditions.Add(new SearchCondition("DepNo", "string" ,"<=","QC99", "and"));
searchPredicate = SearchExtensions.BuildSearchToExpression<Employee>(searchConditions);
System.Console.WriteLine("string >= ,<=");
System.Console.WriteLine(searchPredicate.ToString());
System.Console.WriteLine("");
public class SearchCondition
public string xField { get; set; }
public string xType { get; set; }
public string xOperator { get; set; }
public string xValue { get; set; }
public string xAndOr { get; set; }
public SearchCondition(string field, string type, string operate, string value, string andOr = "and")
public class SearchExtensions
public static Expression<Func<T, bool>> BuildSearchToExpression<T>(List<SearchCondition> conditionList)
Expression<Func<T, bool>> searchExpression = null;
foreach (SearchCondition item in conditionList)
Expression<Func<T, bool>> resultExpression = null;
if (item.xType == "string")
resultExpression = CreateStringLambda<T>(item);
resultExpression = CreateNumericLambda<T>(item);
if (searchExpression == null)
searchExpression = resultExpression;
if ("and".Equals(item.xAndOr.ToLower()))
searchExpression = searchExpression.AndAlso(resultExpression);
searchExpression = searchExpression.OrElse(resultExpression);
private static Expression<Func<T, bool>> CreateStringLambda<T>(SearchCondition condition)
MethodCallExpression methodCallExpression;
UnaryExpression notMethodCallExpression;
ParameterExpression paramExpr = Expression.Parameter(typeof(T), "p");
MemberExpression leftProperty = Expression.Property(paramExpr, condition.xField);
ConstantExpression rightValue = Expression.Constant(condition.xValue);
switch (condition.xOperator.ToLower())
expression = Expression.Equal(leftProperty, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.NotEqual(leftProperty, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.GreaterThanOrEqual(leftProperty, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.LessThanOrEqual(leftProperty, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.GreaterThan(leftProperty, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.LessThan(leftProperty, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
methodInfo = typeof(string).GetMethod("Contains", new Type[] {typeof(string)});
methodCallExpression = Expression.Call(leftProperty, methodInfo, rightValue);
return Expression.Lambda<Func<T, bool>>(methodCallExpression,paramExpr);
methodInfo = typeof(string).GetMethod("Contains", new Type[] {typeof(string)});
methodCallExpression = Expression.Call(leftProperty, methodInfo, rightValue);
notMethodCallExpression = Expression.Not(methodCallExpression);
return Expression.Lambda<Func<T, bool>>(notMethodCallExpression, paramExpr);
methodInfo = typeof(string).GetMethod("StartsWith", new Type[] {typeof(string)});
methodCallExpression = Expression.Call(leftProperty, methodInfo, rightValue);
return Expression.Lambda<Func<T, bool>>(methodCallExpression,paramExpr);
methodInfo = typeof(string).GetMethod("StartsWith", new Type[] {typeof(string)});
methodCallExpression = Expression.Call(leftProperty, methodInfo, rightValue);
notMethodCallExpression = Expression.Not(methodCallExpression);
return Expression.Lambda<Func<T, bool>>(notMethodCallExpression, paramExpr);
methodInfo = typeof(string).GetMethod("EndsWith", new Type[] {typeof(string)});
methodCallExpression = Expression.Call(leftProperty, methodInfo, rightValue);
return Expression.Lambda<Func<T, bool>>(methodCallExpression,paramExpr);
methodInfo = typeof(string).GetMethod("EndsWith", new Type[] {typeof(string)});
methodCallExpression = Expression.Call(leftProperty, methodInfo, rightValue);
notMethodCallExpression = Expression.Not(methodCallExpression);
return Expression.Lambda<Func<T, bool>>(notMethodCallExpression, paramExpr);
expression = Expression.Equal(leftProperty, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
private static Expression<Func<T, bool>> CreateNumericLambda<T>(SearchCondition condition)
ParameterExpression paramExpr = Expression.Parameter(typeof(T), "p");
MemberExpression leftProperty = Expression.Property(paramExpr, condition.xField);
bool isNullable = leftProperty.Type.IsGenericType && leftProperty.Type.GetGenericTypeDefinition() == typeof(Nullable<>);
Type nonNullableType = null;
ConstantExpression rightValue = GetConstantExpression(condition, isNullable, out nonNullableType);
UnaryExpression leftMember = Expression.Convert(leftProperty, nonNullableType);
switch (condition.xOperator)
expression = Expression.Equal(leftMember, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.NotEqual(leftMember, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.GreaterThan(leftMember, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.LessThan(leftMember, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.GreaterThanOrEqual(leftMember, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.LessThanOrEqual(leftMember, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
expression = Expression.Equal(leftMember, rightValue);
return Expression.Lambda<Func<T, bool>>(expression, paramExpr);
private static bool IsNullableType(Type t)
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
private static ConstantExpression GetConstantExpression(SearchCondition condition, bool isNullable, out Type nonNullableType)
ConstantExpression constant = null;
bool isParseSuccess = false;
switch (condition.xType.ToLower())
nonNullableType = typeof(bool);
nonNullableType = typeof(bool);
if (condition.xValue == "1")
constant = Expression.Constant(true);
constant = Expression.Constant(false);
nonNullableType = typeof(int);
nonNullableType = typeof(int);
isParseSuccess = int.TryParse(condition.xValue, out int intValue);
constant = Expression.Constant(intValue);
constant = Expression.Constant(0);
nonNullableType = typeof(decimal);
nonNullableType = typeof(decimal);
isParseSuccess = decimal.TryParse(condition.xValue, out decimal decValue);
constant = Expression.Constant(decValue);
constant = Expression.Constant(0);
nonNullableType = typeof(DateTime);
nonNullableType = typeof(DateTime);
isParseSuccess = DateTime.TryParse(condition.xValue, out DateTime dateValue);
constant = Expression.Constant(dateValue);
constant = Expression.Constant(System.DateTime.Today);
nonNullableType = typeof(Guid);
nonNullableType = typeof(Guid);
isParseSuccess = Guid.TryParse(condition.xValue, out Guid guidValue);
constant = Expression.Constant(guidValue);
constant = Expression.Constant(Guid.Empty);
throw new NotImplementedException("condition type error ");
static class ExpressionExtensions
public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
var paramExpr = Expression.Parameter(typeof(T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], paramExpr);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], paramExpr);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left, right), paramExpr);
public static Expression<Func<T, bool>> OrElse<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
var paramExpr = Expression.Parameter(typeof(T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], paramExpr);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], paramExpr);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(left, right), paramExpr);
private class ReplaceExpressionVisitor : ExpressionVisitor
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
public override Expression Visit(Expression node)
public Guid Id { get; set; }
public string EmpNo { get; set; }
public string EmpName { get; set; }
public string DepNo { get; set; }
public string PosNo { get; set; }
public DateTime Birthday { get; set; }
public DateTime HireDate { get; set; }
public DateTime? QuitDate { get; set; }
public int Salary { get; set; }