【问题标题】:How to know if OrderBy was applied to query?如何知道 OrderBy 是否应用于查询?
【发布时间】:2016-04-29 03:47:44
【问题描述】:

在应用 .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 时,问题结果是 false 应该是 true

有没有更好的方法来了解 OrderBy 是否应用于查询?

【问题讨论】:

  • 你为什么不把方法重载改为只接受一个 IOrderedQueryable 并有另一个重载接受 IQueryable 并调用 orderby(x => true) 然后调用有序重载跨度>

标签: c# entity-framework linq


【解决方案1】:

这是我想到的一种可能的解决方案:

创建一个表达式访问者,检查表达式是否调用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
{
    //..
}

【讨论】:

  • 这正是我想要的!
  • 对于 C# 6,我建议使用 nameof(Queryable.OrderBy) 等来摆脱魔法字符串。
  • @KirkWoll,我同意。但是,我更喜欢在这里(在 stackoverflow 上)使用旧语法,因为我不能假设 OP 或其他读者正在使用 C#6。
  • @KirkWoll,对。我认为使用 C# 4 或 5 是安全的。
  • 当然,很公平。
【解决方案2】:

你不需要知道什么。这是没有意义的。如果您想TakeSkip 一些元素,您需要在此之前订购它们。考虑关注

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 将只花费一次列表。
  • 就我而言,我不需要知道订单,我只需要知道它是否被订购。订单是我图书馆的客户下的。
  • 我没有列表,我有一个 DataContext 女巫会生成对数据库的命中。
  • 如果没有订购,您将应用什么顺序?您应该期望来自客户端的有序结果或无序结果。我相信总有办法重构它。
  • OrderBy(t =&gt; true) 不会为您提供与传递给方法的订单不同的订单。通过这种方式,您只需 TakeSkip 所需的元素数量,而无需检查订单。应该是一样的。
猜你喜欢
  • 1970-01-01
  • 2011-01-22
  • 1970-01-01
  • 2018-11-10
  • 2020-03-09
  • 2014-07-07
  • 2013-10-20
  • 1970-01-01
相关资源
最近更新 更多