在LinQ to SQL构建动态条件表达式时,当构造>、>=、<、<=这4种条件时,当调用
Expression.GreaterThan、Expression.GreaterThanOrEqual、
Expression.LessThan、Expression.LessThanOrEqual
这4个方法时,如果遇到枚举类型属性,就会报错,提示如下:没有为类型“xxx”和“xxx”定义二进制运算符 GreaterThan”,
其中xxx是枚举类型的具体类型。原因好像是因为枚举类型没有实现上述4种操作符。我尝试用CompareTo(value)方法变相实现比较,虽然可以构造表达式,但是在运行时生成SQL失败。请问,有什么办法解决这个问题?
Expression.GreaterThan、Expression.GreaterThanOrEqual、
Expression.LessThan、Expression.LessThanOrEqual
这4个方法时,如果遇到枚举类型属性,就会报错,提示如下:没有为类型“xxx”和“xxx”定义二进制运算符 GreaterThan”,
其中xxx是枚举类型的具体类型。原因好像是因为枚举类型没有实现上述4种操作符。我尝试用CompareTo(value)方法变相实现比较,虽然可以构造表达式,但是在运行时生成SQL失败。请问,有什么办法解决这个问题?
Expression.GreaterThan((int)你的枚举值)
我没有使用过这个语句,将你正在使用,并且报错的语句贴出来看一下
public class FilterSet<T>
{
public static List<ITransformProvider> TransformProviders { get; set; } static FilterSet()
{
TransformProviders = new List<ITransformProvider>
{
new DateBlockTransformProvider(),
new GreaterDateTimeTransformProvider(),
new InTransformProvider(),
new LessDateTimeTransformProvider(),
new LikeTransformProvider()
};
}
public IList<FilterItem> Items { get; set; } = new List<FilterItem>(); public Expression<Func<T, bool>> ToExpression()
{
//构建 c=>Body中的c
ParameterExpression param = Expression.Parameter(typeof(T), "c"); if (Items.Count > 0)
{
//构建c=>Body中的Body
var body = GetExpressoinBody(param, Items);
//将二者拼为c=>Body
return Expression.Lambda<Func<T, bool>>(body, param);
}
return Expression.Lambda<Func<T, bool>>(Expression.Constant(true), param);
}
private Expression GetExpressoinBody(ParameterExpression param, IEnumerable<FilterItem> items)
{
//仅支持全And条件
var list = new List<Expression>();
list.Add(GetGroupExpression(param, items, Expression.AndAlso));
return list.Aggregate(Expression.AndAlso);
} private Expression GetGroupExpression(ParameterExpression param, IEnumerable<FilterItem> items, Func<Expression, Expression, Expression> func)
{
//获取最小的判断表达式
var list = items.Select(item => GetExpression(param, item));
//再以逻辑运算符相连
return list.Aggregate(func);
} private Expression GetExpression(ParameterExpression param, FilterItem item)
{
//属性表达式
LambdaExpression exp = GetPropertyLambdaExpression(item, param);
//如果有特殊类型处理,则进行处理
foreach (var provider in TransformProviders)
{
if (provider.Match(item, exp.Body.Type))
{
return GetGroupExpression(param, provider.Transform(item, exp.Body.Type), Expression.AndAlso);
}
}
//常量表达式
var constant = ChangeTypeToExpression(item, exp.Body.Type);
//以判断符或方法连接
return ExpressionDict[item.Method](exp.Body, constant);
} private LambdaExpression GetPropertyLambdaExpression(FilterItem item, ParameterExpression param)
{
//获取每级属性如c.Users.Proiles.UserId
var props = item.FieldName.Split('.');
Expression propertyAccess = param;
var typeOfProp = typeof(T);
int i = 0;
do
{
PropertyInfo property = typeOfProp.GetProperty(props[i]);
if (property == null)
return null;
typeOfProp = property.PropertyType;
propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
i++;
} while (i < props.Length); return Expression.Lambda(propertyAccess, param);
} #region ChangeType /// <summary>
/// 类型转换,支持非空类型与可空类型之间的转换
/// </summary>
/// <param name="value"></param>
/// <param name="conversionType"></param>
/// <returns></returns>
public static object ChangeType(object value, Type conversionType)
{
if (value == null)
return null;
return Convert.ChangeType(value, TypeUtil.GetUnNullableType(conversionType));
} /// <summary>
/// 转换SearchItem中的Value的类型,为表达式树
/// </summary>
/// <param name="item"></param>
/// <param name="conversionType">目标类型</param>
public static Expression ChangeTypeToExpression(FilterItem item, Type conversionType)
{
if (item.Value == null)
return Expression.Constant(item.Value, conversionType);
#region 数组
if (item.Method == FilterMethod.StandardIn)
{
var arr = (item.Value as Array);
var expList = new List<Expression>();
//确保可用
if (arr != null)
{
for (var i = 0; i < arr.Length; i++)
{
//构造数组的单元Constant
var newValue = ChangeType(arr.GetValue(i), conversionType);
expList.Add(Expression.Constant(newValue, conversionType));
}
}
//构造inType类型的数组表达式树,并为数组赋初值
return Expression.NewArrayInit(conversionType, expList);
} #endregion var elementType = TypeUtil.GetUnNullableType(conversionType); var value = (elementType.IsEnum ? Enum.Parse(conversionType, item.Value.ToString()) : Convert.ChangeType(item.Value, elementType));
//var value = (elementType.IsEnum ? Convert.ChangeType(item.Value, typeof(int)) : Convert.ChangeType(item.Value, elementType)); return Expression.Constant(value, conversionType);
} #endregion #region SearchMethod 操作方法 private static readonly Dictionary<FilterMethod, Func<Expression, Expression, Expression>> ExpressionDict =
new Dictionary<FilterMethod, Func<Expression, Expression, Expression>>
{
{ FilterMethod.Eq, (left, right) => { return Expression.Equal(left, right); } },
{ FilterMethod.Gt, (left, right) =>
{
//if (left.Type.IsEnum)
//{
// var methodInfo = left.Type.GetMethod("CompareTo", new Type[]{ left.Type });
// var value = (int)((ConstantExpression)right).Value;
// var eLeft = Expression.Call(left, methodInfo, Expression.Constant(value, typeof(object)) );
// var eRight = Expression.Constant(0, typeof(int));
// return Expression.GreaterThan(eLeft, eRight);
//}
//var lProp = (LambdaExpression)left;
//var rValue = (int)((ConstantExpression)right).Value;
//var eRight = Expression.Constant(rValue);
return Expression.GreaterThan(left, right);
}
},
{ FilterMethod.Ge, (left, right) => { return Expression.GreaterThanOrEqual(left, right); } },
{ FilterMethod.Lt, (left, right) => { return Expression.LessThan(left, right); } },
{ FilterMethod.Le, (left, right) => { return Expression.LessThanOrEqual(left, right); } },
{ FilterMethod.Contains, (left, right) =>
{
if (left.Type != typeof (string))
return null;
return Expression.Call(left, typeof (string).GetMethod("Contains"), right);
}
},
{ FilterMethod.StandardIn, (left, right) =>
{
if (!right.Type.IsArray)
return null;
//调用Enumerable.Contains扩展方法
return Expression.Call(typeof (Enumerable), "Contains", new[] {left.Type}, right, left);
}
},
{ FilterMethod.Nq, (left, right) => { return Expression.NotEqual(left, right); } },
{ FilterMethod.StartsWith, (left, right) =>
{
if (left.Type != typeof (string))
return null;
return Expression.Call(left, typeof (string).GetMethod("StartsWith", new[] {typeof (string)}), right);
}
},
{ FilterMethod.EndsWith, (left, right) =>
{
if (left.Type != typeof (string))
return null;
return Expression.Call(left, typeof (string).GetMethod("EndsWith", new[] {typeof (string)}), right);
}
},
{ FilterMethod.DateTimeLessThanOrEqual, (left, right) => { return Expression.LessThanOrEqual(left, right); } }
}; #endregion
}代码中标红的语句,会报异常,提示枚举类型没有实现GreaterThan
就是最后的字典实现中的:
{ FilterMethod.Gt, (left, right) =>
{
//if (left.Type.IsEnum)
//{
// var methodInfo = left.Type.GetMethod("CompareTo", new Type[]{ left.Type });
// var value = (int)((ConstantExpression)right).Value;
// var eLeft = Expression.Call(left, methodInfo, Expression.Constant(value, typeof(object)) );
// var eRight = Expression.Constant(0, typeof(int));
// return Expression.GreaterThan(eLeft, eRight);
//}
//var lProp = (LambdaExpression)left;
//var rValue = (int)((ConstantExpression)right).Value;
//var eRight = Expression.Constant(rValue);
return Expression.GreaterThan(left, right);
}
},
返回语句会报异常
static void Main(string[] args)
{
Expression greaterThanExpr = Expression.GreaterThan(
Expression.Constant(1),
Expression.Constant((int)Test.NO)
);
Console.WriteLine(greaterThanExpr.ToString());
Console.WriteLine(
Expression.Lambda<Func<bool>>(greaterThanExpr).Compile()()); greaterThanExpr = Expression.GreaterThan(
Expression.Constant(1),
Expression.Constant((int)Test.YES)
);
Console.WriteLine(greaterThanExpr.ToString());
Console.WriteLine(
Expression.Lambda<Func<bool>>(greaterThanExpr).Compile()()); Console.ReadLine();
} public enum Test
{
NO=0,
YES=1,
}
然后把变量传递给Expression.GreaterThan
因为Expression表达式树无法使用C#方法。
{
//属性表达式
LambdaExpression exp = GetPropertyLambdaExpression(item, param);
//如果有特殊类型处理,则进行处理
...
//常量表达式
var constant = ChangeTypeToExpression(item, exp.Body.Type);
//以判断符或方法连接
return ExpressionDict[item.Method](exp.Body, constant);
}
上面最后一句调用Dictionary,传入的left就是上面的exp.Body,是获取属性的表达式,再看取属性的实现:
private LambdaExpression GetPropertyLambdaExpression(FilterItem item, ParameterExpression param)
{
//获取每级属性如c.Users.Proiles.UserId
var props = item.FieldName.Split('.');
Expression propertyAccess = param;
var typeOfProp = typeof(T);
int i = 0;
do
{
PropertyInfo property = typeOfProp.GetProperty(props[i]);
if (property == null)
return null;
typeOfProp = property.PropertyType;
propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
i++;
} while (i < props.Length);
return Expression.Lambda(propertyAccess, param);
}
这里是通过反射,取运行时传入的某个业务实体类型的的属性,构造一个属性访问表达式,这个属性可能是整形浮点型,可能是枚举,可能是字符串,现在的问题是遇到枚举的时候,GreaterThan报错,因为枚举类型没有实现>、<、>=、<=这4个操作符