如何知道查询中是否使用了 OrderBy?

3

在应用.Skip或.Take之前,我需要知道是否已将OrderBy应用于Linq查询。我无法控制接收到的查询,如果已应用OrderBy,则需要保留此查询,否则我应该OrderBy(t=>true)。我尝试了以下方法:

    DataContext db;
    var query = db.Orders;

    var wasOrderByApplied = typeof(IOrderedQueryable<Order>).IsAssignableFrom(query.AsQueryable().Expression.Type);
    var wasOrderByApplied2 = query.AsQueryable().Expression.Type == typeof(System.Data.Entity.Core.Objects.ObjectQuery<Order>);
    var wasOrderByApplied3 = typeof(IOrderedQueryable<Order>) == query.AsQueryable().Expression.Type;

    var query2 = db.Orders.OrderBy(o => o.CreationDate);

    var wasOrderByApplied4 = typeof(IOrderedQueryable<Order>).IsAssignableFrom(query2.AsQueryable().Expression.Type);
    var wasOrderByApplied5 = query2.AsQueryable().Expression.Type == typeof(System.Data.Entity.Core.Objects.ObjectQuery<Order>);
    var wasOrderByApplied6 = typeof(IOrderedQueryable<Order>) == query2.AsQueryable().Expression.Type;

    var query3 = db.Orders.OrderBy(o => o.CreationDate).Where(o => o.Id > 4);

    var wasOrderByApplied7 = typeof(IOrderedQueryable<Order>).IsAssignableFrom(query3.AsQueryable().Expression.Type);
    var wasOrderByApplied8 = query3.AsQueryable().Expression.Type == typeof(System.Data.Entity.Core.Objects.ObjectQuery<Order>);
    var wasOrderByApplied9 = typeof(IOrderedQueryable<Order>) == query3.AsQueryable().Expression.Type;

结果如下:
wasOrderByApplied = true;
wasOrderByApplied2 = true;
wasOrderByApplied3 = false;

wasOrderByApplied4 = true;
wasOrderByApplied5 = false;
wasOrderByApplied6 = true;

根据最近的结果显示,每个查询所提出的第三个问题是正确的,但我执行了第三个查询(query3),结果如下:
wasOrderByApplied7 = false;
wasOrderByApplied8 = false;
wasOrderByApplied9 = false;

当我在OrderBy之后添加一个Where语句时,查询结果应该是true,但实际上是false。请问有更好的方法来确定是否对查询应用了OrderBy吗?


1
为什么不将方法重载更改为仅接受IOrderedQueryable,并具有另一个重载,该重载接受IQueryable并调用orderby(x => true),然后调用已排序的重载? - johnny 5
2个回答

7
以下是我想到的一种可能的解决方案:
创建一个表达式访问器,检查表达式是否调用了OrderByOrderByDescending方法,例如:
public class MyVisitor : ExpressionVisitor
{
    public bool HasOrderBy { get; private set; }

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Method.DeclaringType == typeof (Queryable) &&
            (node.Method.Name == "OrderBy" || node.Method.Name == "OrderByDescending"))
            HasOrderBy = true;

        return base.VisitMethodCall(node);
    }
}

以下是如何使用它的方法:
MyVisitor visitor = new MyVisitor();

visitor.Visit(query.Expression);

if (visitor.HasOrderBy)
{
    //..
}
else
{
    //..
}

这正是我正在寻找的! - yosbel
2
使用C# 6,我建议使用nameof(Queryable.OrderBy)等方法来消除魔术字符串。 - Kirk Woll
@KirkWoll,我同意您的观点。不过,我更喜欢在这里(stackoverflow)使用旧的语法,因为我不能假设OP或其他读者正在使用C#6。 - Yacoub Massad
@KirkWoll,没错。我认为使用C# 4或5是安全的。 - Yacoub Massad
好的,没问题。 - Kirk Woll
@KirkWoll 在我的非常特殊的情况下,我不能使用C#6,因为它将在不同的环境中使用,我无法确保它可用。 - yosbel

0

你不需要知道什么,这是毫无意义的。如果你想要或者跳过一些元素,你需要在那之前对它们进行排序。请考虑以下内容:

class Animal
{
    public int Weight { get; set; }
    public int NumberOfLegs { get; set; }
}

...

public IEnumerable<Animal> TakeFive(IEnumerable<Animal> animals)
{
    return animals.Take(5);
}

你会选择哪五种动物?那些腿最多的?还是那些重量更大的?或者那些重量更轻的?你不知道。但如果你像这样改变代码

public IEnumerable<Animal> TakeFive(IEnumerable<Animal> animals)
{
    return animals.OrderBy(_ => .Weight).Take(5);
}

开始有些意义了。

不仅可以将相同的应用于 IEnumerable,还可以将其应用于 IQueryable。如果您不知道顺序是什么,那么无需知道序列是否已排序。


我同意这个观点 - 如果IList已经按正确顺序排序,OrderBy只需要对列表进行一次遍历。 - PhillipH
在我的情况下,我不需要知道顺序,我只需要知道它是否被排序过。该订单是由我的库的客户放置的。 - yosbel
我没有一个列表,我有一个 DataContext,它生成对数据库的访问。 - yosbel
如果没有规定顺序,你会采用什么顺序?你应该期望客户端返回有序或无序的结果。我相信总有一种方法可以重构这个问题。 - Maxim Kosov
想象一下,你有一个在表格中显示项目的库。这是一个通用的表格,你不知道项目的类型。客户端知道如何对它们进行排序,而不是我的库。我怎么能对我不知道其类型的东西进行排序呢? - yosbel
显示剩余3条评论

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