基于相关表动态修改LINQ查询

时间:2014-04-18 20:14:07

标签: c# linq expression

我正在根据各种搜索条件动态创建LINQ查询。

作为一个例子,假设我正在搜索Automobiles表,我可以选择按等级进行过滤。我有两个控件:

  1. 比较类型:[至少],[最多],[小于],[大于]和[等于]。
  2. 值:比较评级的值。
  3. 因此,用户可以选择比较类型[至少]和值3,我的代码需要创建一个查询,将结果限制为大于或等于3的汽车评级。

    我在问题How to implement search functionality in C#/ASP.NET MVC中找到了VinayC给出的一个很好的解决方案。他的DynamicWhere()方法动态地创建了可以生成正确过滤器的表达式的一部分。

    我的问题是我的主要查询类型是汽车,但我的评级是在一个单独的表(Automobile.Ratings)中。我如何实现相同的技术并过滤我的主要查询类型以外的类型?

    感谢您的任何提示。

3 个答案:

答案 0 :(得分:2)

由于您拥有的操作数量很少,有限且在comiple时间已知,因此您只需使用switch处理它:

IQueryable<Something> query = GetQuery();

int ratingToComareWith = 1;
string operation = "Equal";

switch (operation)
{
    case ("Equal"):
        query = query.Where(item => item == ratingToComareWith);
        break;
    case ("Less Than"):
        query = query.Where(item => item < ratingToComareWith);
        break;
}

答案 1 :(得分:0)

这是表达式构建的实体框架友好替代方案。当然,您需要验证columnop以防止SQL注入。

// User provided values
int value = 3;
string column = "Rating";
string op = "<";
// Dynamically built query
db.Database.SqlQuery<Automobile>(@"select distinct automobile.* from automobile
    inner join ratings on .... 
    where [" + column + "] " + op + " @p0", value);

答案 2 :(得分:0)

Here是一种为linq-to-entities的嵌套集合或类型构建条件的方法。 根据您的需求进行重组:

        public static Expression GetCondition(Expression parameter, object value, OperatorComparer operatorComparer, params string[] properties)
{
    Expression resultExpression = null;
    Expression childParameter, navigationPropertyPredicate;
    Type childType = null;

    if (properties.Count() > 1)
    {
        //build path
        parameter = Expression.Property(parameter, properties[0]);
        var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type);
        //if it´s a collection we later need to use the predicate in the methodexpressioncall
        if (isCollection)
        {
            childType = parameter.Type.GetGenericArguments()[0];
            childParameter = Expression.Parameter(childType, childType.Name);
        }
        else
        {
            childParameter = parameter;
        }
        //skip current property and get navigation property expression recursivly
        var innerProperties = properties.Skip(1).ToArray();
        navigationPropertyPredicate = GetCondition(childParameter, test, innerProperties);
        if (isCollection)
        {
            //build methodexpressioncall
            var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2);
            anyMethod = anyMethod.MakeGenericMethod(childType);
            navigationPropertyPredicate = Expression.Call(anyMethod, parameter, navigationPropertyPredicate);
            resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
        }
        else
        {
            resultExpression = navigationPropertyPredicate;
        }
    }
    else
    {
       var childProperty = parameter.Type.GetProperty(properties[0]);
       var left = Expression.Property(parameter, childProperty);
       var right = Expression.Constant(value,value.GetType());
       if(!new List<OperatorComparer>    {OperatorComparer.Contains,OperatorComparer.StartsWith}.Contains(operatorComparer))
        {
            navigationPropertyPredicate = Expression.MakeBinary((ExpressionType)operatorComparer,left, right);
        }
        else
        {
            var method = GetMethod(childProperty.PropertyType, operatorComparer); //get property by enum-name from type
            navigationPropertyPredicate = Expression.Call(left, method, right);
        }
        resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
    }
    return resultExpression;
}

private static MethodInfo GetMethod(Type type,OperatorComparer operatorComparer)
{
    var method = type.GetMethod(Enum.GetName(typeof(OperatorComparer),operatorComparer));
    return method;
} 

public enum OperatorComparer
{
    Equals = ExpressionType.Equal,
    Contains,
    StartsWith,
    GreaterThan = ExpressionType.GreaterThan
    ....

}

private static Expression MakeLambda(Expression parameter, Expression predicate)
{
    var resultParameterVisitor = new ParameterVisitor();
    resultParameterVisitor.Visit(parameter);
    var resultParameter = resultParameterVisitor.Parameter;
    return Expression.Lambda(predicate, (ParameterExpression)resultParameter);
}

private class ParameterVisitor : ExpressionVisitor
{
    public Expression Parameter
    {
        get;
        private set;
    }
    protected override Expression VisitParameter(ParameterExpression node)
    {
        Parameter = node;
        return node;
    }
}
    }

如果需要,可以用params Expression(Func(T,object))替换params string []。需要更多的工作才能这样做。您需要使用类似

的语法来定义嵌套集合
item => item.nestedCollection.Select(nested => nested.Property)

并在表达式访问者的帮助下重写表达式。