【问题标题】:Replace OrderBy with Where in LINQ query用 LINQ 查询中的 Where 替换 OrderBy
【发布时间】:2020-04-20 10:44:15
【问题描述】:

CosmosDb 存在一个已知问题,如果您使用 ORDER BY 子句,它会排除未定义此属性的文档

为了解决这个问题,我正在尝试创建一个功能,该功能接受一个 LINQ 查询并将Order 子句替换为检查未定义属性的文档,这样我们就可以运行这两个查询并组合结果。

所以:

ordersDb.Where(x => x.Name == customerName).OrderBy(x => x.CompanyName)

会变成:

ordersDb.Where(x => x.Name == customerName)
.Where(x => !x.CompanyName.IsDefined()) // IsDefined is a built in CosmosDb function

使用表达式生成器,我创建了以下内容。但是,我在尝试将我的表达式称为 Where 方法时遇到问题 - :

private sealed class OrderByToIsNotDefinedVisitor : ExpressionVisitor
{
    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Method.DeclaringType == typeof(Queryable) &&
            (node.Method.Name == "OrderBy" || node.Method.Name == "OrderByDescending"))
        {
            // Get the IsDefined method
            var methodIsDefined = typeof(TypeCheckFunctionsExtensions).GetMethod("IsDefined",
                BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, null,
                new Type[] { typeof(object) }, null);

            // Apply the IsDefined method to the property that was being used for OrderBy
            var isDefinedItem = Expression.Call(methodIsDefined, node.Arguments[1]);

            // Alter the expression to check for !IsDefined()
            var isNotDefinedItem = Expression.Not(isDefinedItem);

            var entityType = node.Method.GetGenericArguments()[0];

            var genericWhere = BuildGenericWhere();

            var methodWhere = genericWhere.MakeGenericMethod(entityType);

            var param = Expression.Parameter(entityType);

            Expression newExpression =
                Expression.Call(
                    methodWhere,
                    node.Arguments[0],
                    Expression.Lambda(
                        typeof(Func<,>).MakeGenericType(entityType, typeof(bool)),
                        isNotDefinedItem,
                        param));

            return newExpression;
        }

        return base.VisitMethodCall(node);
    }
}

private static MethodInfo BuildGenericWhere()
{
    var genericWhereMethod = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static)
        .Where(x => x.Name == "Where" && x.GetGenericArguments().Length == 1)
        .Select(x => new { Method = x, Parameters = x.GetParameters() })
        .Where(x => x.Parameters.Length == 2 &&
                    x.Parameters[0].ParameterType.IsGenericType &&
                    x.Parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>) &&
                    x.Parameters[1].ParameterType.IsGenericType &&
                    x.Parameters[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>))
        .Select(x => x.Method)
        .Single();

    return genericWhereMethod;
}

这编译正常,但是当我执行查询时,我得到:

Microsoft.Azure.Documents.Linq.DocumentQueryException:表达式与 不支持 NodeType Lambda

...因为 documentDb 无法处理 lambda 子句

我也试过用直接调用 where 方法替换 lambda 子句,所以调用变成:

var updatedQueryExpression = Expression.Call(node.Arguments[0], methodWhere, isNotDefinedItem);

return updatedQueryExpression; 

...但是,这会导致:

System.ArgumentException:静态方法需要空实例, 非静态方法需要非空实例

【问题讨论】:

  • 试试:ordersDb.Select(x => x).OrderBy(x => x.CompanyName)
  • 您可能已经考虑过这一点,但您可以通过从查询中删除 ORDER BY 并在客户端对结果进行排序来实现您的目标。这将允许您通过一次调用数据库而不是两次来执行操作。
  • 好建议@Paul,在前端订购会起作用,但我们实际上只想带回前 10 行,这样做可能会为客户端带来大量额外数据
  • 您好,这里有更新吗?我的回答对您有帮助吗?

标签: c# linq azure-cosmosdb expressionbuilder


【解决方案1】:

首先,我们是told,我们只能使用文档的属性进行排序,而不是派生值。

所以,对我来说,我也建议您遵循@Paul 在评论中提到的建议:Querying all the matching data from cosmos db, then try to sort the result list and take the top elements.我相信你已经知道How to get first N elements of a list in C#?这个表达式:

var firstFiveArrivals = myList.OrderBy(i => i.ArrivalTime).Take(5);

由于您必须获取顶级元素,并且如果您的匹配数据集足够大,无论您使用 LINQ 还是 SQL,您都会遇到可以通过Continuation Token 解决的阈值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-03
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    • 2018-03-31
    相关资源
    最近更新 更多