【问题标题】:When is ObjectQuery really an IOrderedQueryable?ObjectQuery 什么时候真的是 IOrderedQueryable?
【发布时间】:2012-01-26 20:50:36
【问题描述】:

应用于实体框架,扩展方法Select()OrderBy()都返回一个ObjectQuery,定义为:

public class ObjectQuery<T> : ObjectQuery, IOrderedQueryable<T>,
    IQueryable<T>, <... more interfaces>

Select() 的返回类型为IQueryable&lt;T&gt;OrderBy 的返回类型为IOrderedQueryable&lt;T&gt;。所以你可以说两者都返回相同的类型但在不同的包装器中。幸运的是,因为现在我们可以在调用OrderBy 之后应用ThenBy

现在是我的问题。

假设我有这个:

var query = context.Plots.Where(p => p.TrialId == 21);

这给了我一个IQueryable&lt;Plot&gt;,这是一个ObjectQuery&lt;Plot&gt;。但它也是一个 IOrderedQueryable:

var b = query is IOrderedQueryable<Plot>; // True!

但还是:

var query2 = query.ThenBy(p => p.Number); // Does not compile.
// 'IQueryable<Plot>' does not contain a definition for 'ThenBy'
// and no extension method 'ThenBy' ....

当我这样做时:

var query2 = ((IOrderedQueryable<Plot>)query).ThenBy(p => p.Number);

它可以编译,但会出现运行时异常:

IQueryable`1[Plot]”类型的表达式不能用于“IOrderedQueryable`1[Plot] ThenBy[Plot,Nullable`1](IOrderedQueryable`1[Plot], Expressions.Expression`1[System.Func`2[Plot,System.Nullable`1[System.Int32]]])”方法的'IOrderedQueryable`1[Plot]'类型参数

执行了强制转换(我检查了),但ThenBy 的参数仍然被视为 IQueryable(这让我有点困惑)。

现在假设某个方法将ObjectQuery&lt;Plot&gt; 作为IQueryable&lt;Plot&gt; 返回给我(例如Select())。如果我想知道在返回的对象上调用ThenBy 是否安全怎么办。如何在不捕获异常的情况下判断 ObjectQuery 是“真实”还是“假”IOrderedQueryable

【问题讨论】:

  • 我建议不要检查具体类型,因为它会将您绑定到您的查询提供程序,这正是IQueryable 避免的。一种方法是快速浏览表达式以检查SortBy() 方法,但我怀疑有人会指出一些更容易的东西。如果没有,请在此处发表评论,我将为您提供遍历树的代码。祝你好运
  • @Smudge202 谢谢。有很多地方比表情树更有趣,但如果你无法抗拒……但是,是的,也许是其他人。
  • @Smudge202 顺便说一句,检查具体类型无论如何都不起作用,因为它实现了两个接口。让我感到困惑的是,普通的反射在这里没有帮助,所以如果我真的需要知道的话,可能按照你的建议进行更深入的检查是唯一的方法。
  • 是的,我认为通过检查具体类型是不可能的——也许是一种节省? =) 祝你好运进行更深入的检查。我怀疑通过某种形式的反射可能是可能的,但通过表达式树的建议绝对可能。祝你好运。

标签: c# linq entity-framework-4 interface


【解决方案1】:

表达树真的很有趣! (或者也许我有点怪胎)并且如果Project Roslyn 可以通过的话,它可能会在许多开发人员的未来变得有用! =)

在您的情况下,简单地从 MSDN 的 ExpressionVisitor 继承,并覆盖继承类中的 VisitMethodCall 方法,并将 m.MethodInfoSortBy 进行比较(即,如果您不太挑剔,只需检查名称,如果您想挑剔,请使用反射来获取实际的 SortBy MethodInfo 进行比较。

如果您需要示例,请告诉我/什么,但老实说,在复制/粘贴 ExpressionVisitor 后,您可能需要的非表达式树代码不超过 10 行;-)

希望有帮助

【讨论】:

  • 你做到了。你用表达树病毒感染了我!这是一个可行的解决方案(顺便说一句,方法名称是“OrderBy”)尽管特定于表达式,你是对的,这很有趣!到目前为止我在公园里散步:)。虽然这在这里有效,但我也会发布一个用纯 C# 术语表达问题的问题。
  • 感兴趣的人:here 是纯 C# 问题。
【解决方案2】:

虽然表达式树很有趣,但在这种情况下,简单的解决方案不是使用 OrderBy 而不是 ThenBy 吗?

  • OrderByIQueryable 的扩展并返回 IOrderedQueryable
  • ThenByIOrderedQueryable 的扩展并返回 IOrderedQueryable

因此,如果您有一个 IQueryable(如上述情况,其中 query 是一个 IQueryable)并且您想对其应用初始排序,请使用 OrderByThenBy 仅用于对已排序的查询应用额外的排序。

如果您有某种 LINQ 结果,但您不确定它是 IQueryable 还是 IOrderedQueryable 并且想要对其应用额外的过滤,您可以使用以下两种方法:

 static IOrderedQueryable<T, TKey> ApplyAdditionalOrdering<T, TKey>(this IOrderedQueryable<T, TKey> source, Expression<Func<T, TFilter>> orderBy)
        {
            return source.ThenBy(orderBy);
        }

static IOrderedQueryable<T, TKey> ApplyAdditionalOrdering<T, TKey>(this IQueryable<T> source, Expression<Func<T, TFilter>> orderBy)
        {
            return source.OrderBy(orderBy);
        }

编译器会根据查询对象的编译时类型找出正确的调用对象。

【讨论】:

  • 这是一个聪明的解决方案。一个缺点,它不能在inside中使用和表达。同时,我从来没有真正需要知道IQueryable 是否进行了初始排序。只是我对这个无操作界面感到困惑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 1970-01-01
  • 1970-01-01
  • 2014-04-26
  • 1970-01-01
  • 1970-01-01
  • 2015-06-04
相关资源
最近更新 更多