为了更好的处理高级搜索功能,可以根据用户自定义搜索条件执行动态表达式,以下是动态表达式扩展的核心代码,请铁子们验收,下边还总结了重要的核心知识点,各位可以点击进行阅读学习。
核心知识点:Expression、反射
//自定义对象
public class AdvanceSearchInput
{
public enum SqlOperator
{
Contains,
NotContains,
Equal,
NotEqual,
Greater,
GreaterEqual,
Less,
LessEqual,
InSetOf,
NotInSetOf
}
public string FieldName
{
get;
set;
}
public string FieldValue
{
get;
set;
}
public SqlOperator Operator
{
get;
set;
}
public bool AndAlso
{
get;
set;
}
}
//核心代码扩展
public static class ExpressionExtension
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) => first.AndAlso<T>(second, Expression.AndAlso);
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) => first.AndAlso<T>(second, Expression.OrElse);
private static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2, Func<Expression, Expression, BinaryExpression> func)
{
var parameter = Expression.Parameter(typeof(T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(func(left, right), parameter);
}
private class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression oldValue;
private readonly Expression newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
this.oldValue = oldValue;
this.newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == this.oldValue)
{
return this.newValue;
}
return base.Visit(node);
}
}
}
public class ExpressionVisitorExtensions : Collection<AdvanceSearchInput>
{
public Expression<Func<T, bool>> AsExpression<T>() where T : class
{
var type = typeof(T);
var typeInfo = type.GetTypeInfo();
var parameter = Expression.Parameter(type, "m");
Expression expression = null;
static Expression func(bool andAlso, Expression exp1, Expression exp2)
{
if (exp1 == null)
{
return exp2;
}
return andAlso ? Expression.AndAlso(exp1, exp2) : Expression.OrElse(exp1, exp2);
}
foreach (var item in this)
{
var property = typeInfo.GetProperty(item.FieldName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (property == null || !property.CanRead || string.IsNullOrWhiteSpace(item.FieldValue))
{
continue;
}
var valueType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
Expression<Func<object>> valueLambda = () => GetValue(item.FieldValue, valueType);
switch (item.Operator)
{
case AdvanceSearchInput.SqlOperator.Equal:
if (valueType == typeof(string))
{
var nameToLower = Expression.Call(Expression.Property(parameter, item.FieldName), typeof(String).GetMethod("ToLower", new Type[] { }));
var valueToLower = Expression.Call(Expression.Convert(valueLambda.Body, property.PropertyType), typeof(String).GetMethod("ToLower", new Type[] { }));
expression = func(item.AndAlso, expression, Expression.Call(nameToLower, "Equal", null, valueToLower));
}
else
{
expression = func(item.AndAlso, expression, Expression.Equal(Expression.Property(parameter, item.FieldName), valueType.IsEnum ? Expression.Constant(Enum.Parse(valueType, item.FieldValue)) : Expression.Convert(valueLambda.Body, property.PropertyType)));
}
break;
case AdvanceSearchInput.SqlOperator.NotEqual:
if (valueType == typeof(string))
{
var nameToLower = Expression.Call(Expression.Property(parameter, item.FieldName), typeof(String).GetMethod("ToLower", new Type[] { }));
var valueToLower = Expression.Call(Expression.Convert(valueLambda.Body, property.PropertyType), typeof(String).GetMethod("ToLower", new Type[] { }));
expression = func(item.AndAlso, expression, Expression.Call(nameToLower, "NotEqual", null, valueToLower));
}
else
{
expression = func(item.AndAlso, expression, Expression.NotEqual(Expression.Property(parameter, item.FieldName), Expression.Convert(valueLambda.Body, property.PropertyType)));
}
break;
case AdvanceSearchInput.SqlOperator.Contains:
var nullCheck = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, Expression.Property(parameter, item.FieldName)));
var afterToLower = Expression.Call(Expression.Property(parameter, item.FieldName), typeof(String).GetMethod("ToLower", new Type[] { }));
var beforeToLower = Expression.Call(Expression.Convert(valueLambda.Body, property.PropertyType), typeof(String).GetMethod("ToLower", new Type[] { }));//ToUpper
var contains = Expression.Call(afterToLower, "Contains", null, beforeToLower);
expression = func(item.AndAlso, expression, Expression.AndAlso(nullCheck, contains));
break;
case AdvanceSearchInput.SqlOperator.NotContains:
var nullCheck_n = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, Expression.Property(parameter, item.FieldName)));
var fieldNameToLower = Expression.Call(Expression.Property(parameter, item.FieldName), typeof(String).GetMethod("ToLower", new Type[] { }));
var fieldValueToLower = Expression.Call(Expression.Convert(valueLambda.Body, property.PropertyType), typeof(String).GetMethod("ToLower", new Type[] { }));//ToUpper
var contains_n = Expression.Call(fieldNameToLower, "Contains", null, fieldValueToLower);
expression = func(item.AndAlso, expression, Expression.Not(Expression.AndAlso(nullCheck_n, contains_n)));
break;
case AdvanceSearchInput.SqlOperator.Greater:
expression = func(item.AndAlso, expression, Expression.GreaterThan(Expression.Property(parameter, item.FieldName), Expression.Convert(valueLambda.Body, property.PropertyType)));
break;
case AdvanceSearchInput.SqlOperator.GreaterEqual:
expression = func(item.AndAlso, expression, Expression.GreaterThanOrEqual(Expression.Property(parameter, item.FieldName), Expression.Convert(valueLambda.Body, property.PropertyType)));
break;
case AdvanceSearchInput.SqlOperator.Less:
expression = func(item.AndAlso, expression, Expression.LessThan(Expression.Property(parameter, item.FieldName), Expression.Convert(valueLambda.Body, property.PropertyType)));
break;
case AdvanceSearchInput.SqlOperator.LessEqual:
expression = func(item.AndAlso, expression, Expression.LessThanOrEqual(Expression.Property(parameter, item.FieldName), Expression.Convert(valueLambda.Body, property.PropertyType)));
break;
case AdvanceSearchInput.SqlOperator.InSetOf:
Expression leftExpression = null;
if (valueType.IsEnum)
{
var values = item.FieldValue.Split(",").Select(s => (int)Enum.Parse(valueType, s)).ToList();
leftExpression = Expression.Constant(values, values.GetType());
expression = func(item.AndAlso, expression, Expression.Call(leftExpression, "Contains", null, Expression.Convert(Expression.Property(parameter, item.FieldName), typeof(int))));
}
else
{
if (valueType == typeof(int))
{
List<int> values = item.FieldValue.Split(",").Select(s => Convert.ToInt32(s)).ToList();
leftExpression = Expression.Constant(values, values.GetType());
}
else if (valueType == typeof(string))
{
List<string> values = item.FieldValue.Split(",").ToList<String>();
leftExpression = Expression.Constant(values, values.GetType());
}
else if (valueType == typeof(long))
{
List<long> values = item.FieldValue.Split(",").Select(s => Convert.ToInt64(s)).ToList();
leftExpression = Expression.Constant(values, values.GetType());
}
else if (valueType == typeof(float) || valueType == typeof(double))
{
List<double> values = item.FieldValue.Split(",").Select(s => Convert.ToDouble(s)).ToList();
leftExpression = Expression.Constant(values, values.GetType());
}
if(leftExpression != null)
expression = func(item.AndAlso, expression, Expression.Call(leftExpression, "Contains", null, Expression.Property(parameter, item.FieldName)));
}
break;
case AdvanceSearchInput.SqlOperator.NotInSetOf:
Expression leftExpressionNo = null;
if (valueType.IsEnum)
{
var values = item.FieldValue.Split(",").Select(s => (int)Enum.Parse(valueType, s)).ToList();
leftExpressionNo = Expression.Constant(values, values.GetType());
expression = func(item.AndAlso, expression, Expression.Not(Expression.Call(leftExpressionNo, "Contains", null, Expression.Convert(Expression.Property(parameter, item.FieldName), typeof(int)))));
}
else
{
if (valueType == typeof(int))
{
List<int> values = item.FieldValue.Split(",").Select(s => Convert.ToInt32(s)).ToList();
leftExpressionNo = Expression.Constant(values, values.GetType());
}
else if (valueType == typeof(string))
{
List<string> values = item.FieldValue.Split(",").ToList<String>();
leftExpressionNo = Expression.Constant(values, values.GetType());
}
else if (valueType == typeof(long))
{
List<long> values = item.FieldValue.Split(",").Select(s => Convert.ToInt64(s)).ToList();
leftExpressionNo = Expression.Constant(values, values.GetType());
}
else if (valueType == typeof(float) || valueType == typeof(double))
{
List<double> values = item.FieldValue.Split(",").Select(s => Convert.ToDouble(s)).ToList();
leftExpressionNo = Expression.Constant(values, values.GetType());
}
if (leftExpressionNo != null)
expression = func(item.AndAlso, expression, Expression.Not(Expression.Call(leftExpressionNo, "Contains", null, Expression.Property(parameter, item.FieldName))));
}
break;
}
}
if (expression == null)
{
Expression<Func<T, bool>> defaultExpression = x => true;
return defaultExpression;
}
return (Expression<Func<T, bool>>)Expression.Lambda(expression, parameter);
}
}
private object GetValue(string value, Type type)
{
if (type == typeof(bool))
{
bool boolValue;
if (bool.TryParse(value, out boolValue))
return boolValue;
else
{
int intValue;
if (int.TryParse(value, out intValue))
return Convert.ChangeType(intValue, type);
else
return null;
}
}
else
{
return System.Convert.ChangeType(value, type);
}
}
//使用案例
ExpressionVisitorExtensions advanceSearchInputs = new ExpressionVisitorExtensions();
foreach (var item in listCondition.AdvanceConditions)
{
advanceSearchInputs.Add(item);
}
var reply = this.DBContext.T.Where(advanceSearchInputs.AsExpression<T>())