C# / .NET Entity Framework 中的动态查询构建器

5

假设我们正在使用EF6,db是EF DbContext。 假设用户可以输入五个可能的查询:

  1. 获取汽车年龄等于10岁的记录。
  2. 获取汽车年龄小于10岁的记录。
  3. 获取汽车年龄大于10岁的记录。
  4. 获取汽车年龄不等于10岁的记录。
  5. 获取所有汽车记录。
枚举操作符
{
    相等,
    不相等,    
    小于, 
    大于,
    任意
};
var 操作符 = //用户选择的操作符,从上面的枚举中选择 var 车龄 = //用户选择的整数,指定所需年龄 List 车辆;
switch(操作符) { case 相等: { 车辆 = db.Cars.Where(c=> c.Age == 车龄).ToList(); } case 不相等: { 车辆 = db.Cars.Where(c=> c.Age != 车龄).ToList(); } case 小于: { 车辆 = db.Cars.Where(c=> c.Age < 车龄).ToList(); } case 大于: { 车辆 = db.Cars.Where(c=> c.Age > 车龄).ToList(); } case 任意: { 车辆 = db.Cars.ToList(); } }

如何更有效地做到这一点? switch操作符看起来很傻。 this article 看起来在回答这个问题,但我不完全理解。

我们已经在动态构建多个查询方面有了答案,但那是我想要的不同部分(如果问题是“用户可以搜索汽车年龄和里程数,也可以不搜索”)。
编辑:为了回应评论澄清,我的“目标”是任何标准搜索,以汽车为例,用户将拥有许多字段,可以选择运算符。他们可以使用任何、所有或没有字段,例如:
里程数:运算符,值 年份:运算符,值 品牌:(例如是/不是),汽车品牌

首先,你想做什么?如果你需要很多动态查询,你有几个选择:1. OData,2. 动态 Linq,3. 构建自己的表达式树。很可能你应该只使用动态 Linq,或者编写自己的表达式树,但是学习这些可能会很复杂,如果你有时间限制,你应该使用动态 Linq。 - johnny 5
这实际上只是个人好奇心,想知道“这应该如何正确完成”。使用MongoDb C#驱动程序很容易解决,但似乎在这个主要的商业语言/框架中却如此复杂。我会更新问题。另外,我会查看动态linq,甚至不确定它是什么。OData并不是我想要的。我们在SQL查询中使用它进行分页等操作,但我发现它无法处理许多基本操作。 - VSO
1个回答

7
    Expression<Func<T, bool>> Build<T, TProperty>(Expression<Func<T, TProperty>> property, Operator op, TProperty value)
    {
        if (op == Operator.ANY)
        {
            return e => true;
        }

        var left = property.Body;   //you need to check if it's a valid property visit
        var right = Expression.Constant(value);
        BinaryExpression body;

        switch (op)
        {
            case Operator.EQUAL:
                body = Expression.Equal(left, right);
                break;
            case Operator.NOT_EQUAL:
                body = Expression.NotEqual(left, right);
                break;
            case Operator.LESS_THAN:
                //you'd better check if < operator is available for the type
                body = Expression.LessThan(left, right);
                break;
            case Operator.MORE_THAN:
                body = Expression.GreaterThan(left, right);
                break;
            default: throw new NotSupportedException();
        }

        return Expression.Lambda<Func<T, bool>>(body, property.Parameters[0]);
    }

使用方法 var bmw = Build<Car, string>(c => c.Brand, Operator.EQUAL, "BMW");

这行代码是一个泛型函数的调用,其中第一个参数是一个 lambda 函数,指定了对 Car 类型对象的 Brand 属性进行操作,第二个参数是一个枚举类型 Operator,表示等于号操作符,第三个参数是字符串类型的值 "BMW"。

你能否解释一下这个答案,因为我正在研究类似的解决方案,或者指向某种文章/文档? - Dawood Awan
@DawoodAwan,这是一个漫长的故事,试着从表达式树开始。 - Cheng Chen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接