所以,這很複雜。ORing LINQ查詢,使用表達式樹構建
我在一個集合中有一組規則,一條規則包含這三個屬性。
Field, Op, and Data (all strings)
所以規則可能看起來像「國家」,「EQ」,「CA」
我的一般規則是,所有的規則都與運算在一起。但是,有一點需要注意的是,如果它們具有相同的字段值,那麼所有的ORed。這允許我們說「狀態」,「eq」,「CA」或「狀態」,「eq」,「TX」和「FirstName」,「eq」,「John」。
問題是,我現在應用規則的方式不起作用,因爲它只是使用每個規則來構建linq表達式,以使其更加明確。
var result = rules.Aggregate(_repository.All, (current, rule) => current.ExtendQuery(rule))
ExtendQuery
是一個擴展方法我寫的,它使用ExpressionTrees,生成一個新的查詢,適用目前的規則查詢傳遞。 (有效地使它們全部在一起)
現在,我不難修改.Aggregate
這一行來按字段對規則進行分組,然後爲每個字段生成一個唯一查詢,但是如何獲取它到「或」他們在一起,而不是「和」?
然後對每個這些查詢,我將如何「與」他們在一起?聯盟?
ExtendQuery看起來像這樣
public static IQueryable<T> ExtendQuery<T>(this IQueryable<T> query, QueryableRequestMessage.WhereClause.Rule rule) where T : class
{
var parameter = Expression.Parameter(typeof(T), "x");
Expression property = Expression.Property(parameter, rule.Field);
var type = property.Type;
ConstantExpression constant;
if (type.IsEnum)
{
var enumeration = Enum.Parse(type, rule.Data);
var intValue = (int)enumeration;
constant = Expression.Constant(intValue);
type = typeof(int);
//Add "Id" by convention, this is all because enum support is lacking at this point in Entity Framework
property = Expression.Property(parameter, rule.Field + "Id");
}
else if(type == typeof(DateTime))
{
constant = Expression.Constant(DateTime.ParseExact(rule.Data, "dd/MM/yyyy", CultureInfo.CurrentCulture));
}
else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
//This will convert rule.Data to the baseType, not a nullable type (because that won't work)
var converter = TypeDescriptor.GetConverter(type);
var value = converter.ConvertFrom(rule.Data);
constant = Expression.Constant(value);
//We change the type of property to get converted to it's base type
//This is because Expression.GreaterThanOrEqual can't compare a decimal with a Nullable<decimal>
var baseType = type.GetTypeOfNullable();
property = Expression.Convert(property, baseType);
}
else
{
constant = Expression.Constant(Convert.ChangeType(rule.Data, type));
}
switch (rule.Op)
{
case "eq": //Equals
case "ne": //NotEquals
{
var condition = rule.Op.Equals("eq")
? Expression.Equal(property, constant)
: Expression.NotEqual(property, constant);
var lambda = Expression.Lambda(condition, parameter);
var call = Expression.Call(typeof(Queryable), "Where", new[] { query.ElementType }, query.Expression, lambda);
query = query.Provider.CreateQuery<T>(call);
break;
}
case "lt": //Less Than
query = type == typeof (String)
? QueryExpressionString(query, Expression.LessThan, type, property, constant, parameter)
: QueryExpression(query, Expression.LessThan, property, constant, parameter); break;
case "le": //Less Than or Equal To
query = type == typeof (String)
? QueryExpressionString(query, Expression.LessThanOrEqual, type, property, constant, parameter)
: QueryExpression(query, Expression.LessThanOrEqual, property, constant, parameter); break;
case "gt": //Greater Than
query = type == typeof (String)
? QueryExpressionString(query, Expression.GreaterThan, type, property, constant, parameter)
: QueryExpression(query, Expression.GreaterThan, property, constant, parameter); break;
case "ge": //Greater Than or Equal To
query = type == typeof (String)
? QueryExpressionString(query, Expression.GreaterThanOrEqual, type, property, constant, parameter)
: QueryExpression(query, Expression.GreaterThanOrEqual, property, constant, parameter); break;
case "bw": //Begins With
case "bn": //Does Not Begin With
query = QueryMethod(query, rule, type, "StartsWith", property, constant, "bw", parameter); break;
case "ew": //Ends With
case "en": //Does Not End With
query = QueryMethod(query, rule, type, "EndsWith", property, constant, "cn", parameter); break;
case "cn": //Contains
case "nc": //Does Not Contain
query = QueryMethod(query, rule, type, "Contains", property, constant, "cn", parameter); break;
case "nu": //TODO: Null
case "nn": //TODO: Not Null
break;
}
return query;
}
private static IQueryable<T> QueryExpression<T>(
IQueryable<T> query,
Func<Expression, Expression, BinaryExpression> expression,
Expression property,
Expression value,
ParameterExpression parameter
) where T : class
{
var condition = expression(property, value);
var lambda = Expression.Lambda(condition, parameter);
var call = Expression.Call(typeof(Queryable), "Where", new[] { query.ElementType }, query.Expression, lambda);
query = query.Provider.CreateQuery<T>(call);
return query;
}
private static IQueryable<T> QueryExpressionString<T>(
IQueryable<T> query,
Func<Expression, Expression, BinaryExpression> expression,
Type type,
Expression property,
Expression value,
ParameterExpression parameter)
{
var containsmethod = type.GetMethod("CompareTo", new[] { type });
var callContains = Expression.Call(property, containsmethod, value);
var call = expression(callContains, Expression.Constant(0, typeof(int)));
return query.Where(Expression.Lambda<Func<T, bool>>(call, parameter));
}
private static IQueryable<T> QueryMethod<T>(
IQueryable<T> query,
QueryableRequestMessage.WhereClause.Rule rule,
Type type,
string methodName,
Expression property,
Expression value,
string op,
ParameterExpression parameter
) where T : class
{
var containsmethod = type.GetMethod(methodName, new[] { type });
var call = Expression.Call(property, containsmethod, value);
var expression = rule.Op.Equals(op)
? Expression.Lambda<Func<T, bool>>(call, parameter)
: Expression.Lambda<Func<T, bool>>(Expression.IsFalse(call), parameter);
query = query.Where(expression);
return query;
}
您是否考慮過在字段名稱上使用分組,然後檢查是否存在多個使用「OR」?如果只有一個存在於組中,那麼正常處理? – Sam 2013-02-11 17:54:01
但我不知道如何寫一個「或」 – CaffGeek 2013-02-11 17:57:35