使用表达式树创建动态LINQ查询

时间:2019-02-04 21:50:20

标签: c# sql linq

我无法复制多个。在使用Expression Trees的linq查询中,我需要此查询是动态的,因为它具有搜索功能。我要查询的数据库最终会很大,所以我需要高效且无法过滤客户端。我至少尝试从这些列中的3个开始过滤。在下面,我添加了三个我试图过滤的ID。我已经读了很多表达式树,但是可以使用别人的帮助将它们实际应用到即时通讯中,因为它们使我感到困惑。

    public int? heightId { get; set; }
    public int? ageId { get; set; }
    public int? genderId { get; set; }

2 个答案:

答案 0 :(得分:1)

它不是动态的,因为您事先知道各列。您也不需要使用表达式树。您可以有条件地应用过滤器:

public Class[] FindByFilter(int limit, int? heightId = null, int? ageId = null, int? genderId = null)
{
    var classes = databaseContext.Set<Class>();

    if (heightId.HasValue)
        classes = classes.Where(c => c.HeightId == heightId.Value);

    if (ageId.HasValue)
        classes = classes.Where(c => c.AgeId == ageId.Value);

    if (heightId.HasValue)
        classes = classes.Where(c => c.GenderId == genderId.Value);    

    return classes.Take(limit).ToArray();
}

因此,现在FindByFilter(10, 1, null, 2)或等效的FindByFilter(10, heightId: 1, genderId: 2)将返回前10行,行高1,性别2,但不限年龄。

答案 1 :(得分:0)

因此,您有一类(或几个类)的属性数量有限,可能少于100。

在代码中的某些地方,这可能在用户界面中,但是也可能是在读取互联网页面,文件或从数据库中获取信息之后,在代码中的某处,您将获得一些输入可以决定必须执行哪个Where

因此,在代码中的某个地方,您有一个函数,该函数接受一些输入并返回其中的表达式:

Expression<Func<MyTable, bool>> CreateExpression(MyInput input) {...}

问题是,我们不知道您如何获得输入。我们所知道的是,只有有限的(少于前面提到的100)可能的输出。如果输入有错误,则该过程应将其更正为最佳猜测输出,或者拒绝产生输出。

无论如何,这个漫长的故事是要告诉您,应该创建表达式的代码相对有限。如果您不希望它成为动态的,而是创建一个非动态的函数,即使您有一个大的switch语句(包含50个案例),则可以更好地理解,可测试,对其进行维护。

例如:假设操作员必须输入要过滤的属性的名称和值。在输入错误的情况下,他可能会警告操作员

Expression<Func<MyTable, bool>> CreateExpression(string input, int value)
{
     // TODO: make the procedure case insensitive
    switch (input)
    {
        case "HeightId":
             return myItem => myItem.HeightId == value;
        case "AgeId":
             return myItem => myItem.AgeId == value;
        ...
        default:
            WarnUserIncorrectInput(...)
            return null;
    }
}

您期望多少宗案件?即使有20个,与使用某种魔术解释创建表达式相比,该代码也更容易理解和检查错误输入。毕竟:这种魔术解释还应该检查输入是否错误。

如果将来要添加或删除属性,那么维护此功能将非常容易。

复杂的表达式

可能是希望操作员使用某种定义的语言来组合表达式,例如:

"HeightId == 4 AND AgeId == 10 OR ..."

如果您选择这种投入,将会使您的生活变得非常困难。为此,您几乎必须构建某种编译器,检查各种错误输入。您必须检测出运算符何时表示属性,何时运算符或比较器。不容易做到。

如果您确实希望组合表达式,可以考虑使用诸如组合框之类的用户界面元素,用户必须在其中选择一个属性,然后键入正确的值类型。

由于属性的数量有限,因此可以使用上述过程创建表达式。组合如下所示:

ExpressionFunc<TSource, bool>> CreateAnd<TSource>(
    Expression<Func<TSource, bool>> X,
    Expression<Func<TSource, bool>> Y)
{
     return (sourceElement) => X(sourceElement) && Y(sourceElement);
}

再次:即使您希望让操作员组合语句,如果使用组合框或类似框,也不会得到错误的输入,并且组合的数量也相当有限。

结论

尽管可以从字符串输入创建表达式,但这意味着您必须创建类似编译器的工具来检查错误的输入。这不容易理解,测试和维护。

由于请求的Where语句的数量相当有限(可能少于100个),请考虑使用具有大切换条件的函数