AsQueryable() 的目的是啥?

Posted

技术标签:

【中文标题】AsQueryable() 的目的是啥?【英文标题】:What is the purpose of AsQueryable()?AsQueryable() 的目的是什么? 【发布时间】:2021-11-05 12:15:45 【问题描述】:

AsQueryable() 的目的是为了让您可以将IEnumerable 传递给可能需要IQueryable 的方法,还是有一个有用的理由将IEnumerable 表示为IQueryable?例如,是否应该用于这样的情况:

IEnumerable<Order> orders = orderRepo.GetAll();

// I don't want to create another method that works on IEnumerable,
// so I convert it here.
CountOrders(orders.AsQueryable());

public static int CountOrders(IQueryable<Order> ordersQuery)

    return ordersQuery.Count();

或者它真的让它做一些不同的事情:

IEnumerable<Order> orders = orderRepo.GetAll();
IQueryable<Order> ordersQuery = orders.AsQueryable();

IEnumerable<Order> filteredOrders = orders.Where(o => o.CustomerId == 3);
IQueryable<Order> filteredOrdersQuery = ordersQuery.Where(o => o.CustomerId == 3);

// Are these executed in a different way?
int result1 = filteredOrders.Count();
int result2 = filteredOrdersQuery.Count();

这些扩展方法的IQueryable 版本是否只是构建了一个表达式,一旦执行,它最终会做同样的事情?我的主要问题是,使用AsQueryable 的真正用例是什么?

【问题讨论】:

【参考方案1】:

有几个主要用途。

    正如其他答案中提到的,您可以使用它来模拟使用内存数据源的可查询数据源,以便您可以更轻松地测试最终将在基于不可枚举的 @987654321 上使用的方法@。

    您可以编写辅助方法来操作可应用于内存序列或外部数据源的集合。如果您编写帮助方法以完全使用IQueryable,您可以在所有可枚举项上使用AsQueryable 来使用它们。这使您可以避免编写非常通用的辅助方法的两个单独版本。

    它允许您将可查询的编译时类型更改为IQueryable,而不是更多派生类型。有效;您将在 IQueryable 上使用它,同时在 IEnumerable 上使用 AsEnumerable。您可能有一个实现 IQueryable 的对象,但它也有一个实例 Select 方法。如果是这种情况,并且您想使用 LINQ Select 方法,则需要将对象的编译时类型更改为 IQueryable。您可以直接转换它,但是通过使用 AsQueryable 方法,您可以利用类型推断。如果泛型参数列表很复杂,这会更方便,如果任何泛型参数是匿名类型,这实际上是必要的

【讨论】:

感谢复兴。将其标记为答案,因为它是最完整的。 鉴于IQueryable&lt;T&gt; 派生自IEnumerable&lt;T&gt;,您能解释一下“基于不可枚举的 IQueryable”是什么意思吗? @Dai 它指的是IQueryable,它不仅仅是一个包装好的IEnumerable,它实际上利用了IQueryable 的附加功能。 #1 正是我想要的。感谢您的验证。【参考方案2】:

我对 AsQueryable 最有效的案例是单元测试。假设我有以下有点做作的例子

public interface IWidgetRepository

   IQueryable<Widget> Retrieve();
 

public class WidgetController

   public IWidgetRepository WidgetRepository get; set;


   public IQueryable<Widget> Get()
   
      return WidgetRepository.Retrieve();
   

我想编写一个单元测试来确保控制器传回从存储库返回的结果。它看起来像这样:

[TestMethod]
public void VerifyRepositoryOutputIsReturned()
    
    var widget1 = new Widget();
    var widget2 = new Widget();
    var listOfWidgets = new List<Widget>() widget1, widget2;
    var widgetRepository = new Mock<IWidgetRepository>();
    widgetRepository.Setup(r => r.Retrieve())
      .Returns(listOfWidgets.AsQueryable());
    var controller = new WidgetController();
    controller.WidgetRepository = widgetRepository.Object;

    var results = controller.Get();

    Assert.AreEqual(2, results.Count());
    Assert.IsTrue(results.Contains(widget1));
    Assert.IsTrue(results.Contains(widget2));

实际上,所有 AsQueryable() 方法允许我做的就是在设置模拟时满足编译器的要求。

不过,我会对它在应用程序代码中的使用位置感兴趣。

【讨论】:

【参考方案3】:

正如 sanjuro 所说,Using AsQueryable With Linq To Objects And Linq To SQL 中解释了 AsQueryable() 的用途。文章特别指出,

这在实际场景中提供了极好的好处,其中您在实体上有某些方法返回 T 的 IQueryable,而某些方法返回 List。但是,您有需要应用于所有集合的业务规则过滤器,无论该集合是作为 T 的 IQueryable 还是 T 的 IEnumerable 返回的。从性能的角度来看,您真的希望利用在数据库上执行业务过滤器,如果集合实现 IQueryable 否则回退到使用 Linq 将内存中的业务过滤器应用于委托的对象实现。

【讨论】:

【参考方案4】:

本文Using AsQueryable With Linq To Objects And Linq To SQL详细解释了AsQueryable()的用途

来自 MSDN Queryable.AsQueryable 方法的备注部分:

如果source的类型实现了IQueryable,AsQueryable(IEnumerable)直接返回。否则,它返回一个 IQueryable,它通过调用 Enumerable 中的等效查询运算符方法而不是 Queryable 中的方法来执行查询。

这正是上面文章中提到和使用的内容。 在您的示例中,这取决于 orderRepo.GetAll 返回的内容、IEnumerable 或 IQueryable(Linq to Sql)。如果它返回 IQueryable,Count() 方法将在数据库上执行,否则它将在内存中执行。仔细查看参考文章中的示例。

【讨论】:

【参考方案5】:

接口IQueryable引用文档:

IQueryable 接口旨在通过查询实现 供应商。

因此,对于打算在 .NET 中使其数据结构可查询的人,可以枚举不需要的数据结构或具有有效的枚举器

IEnumerator 是一个用于迭代和处理数据流的接口。

【讨论】:

您介意改写一下吗:“可以枚举不需要的数据结构或具有有效的枚举器”。我了解IQueryableIEnumerable 之间的区别,但我不了解AsQueryable 扩展方法的用例。不过,我对这句话有点困惑,我怀疑这可能是我正在寻找的答案(基于赞成票)。 @Ocelot20:这意味着实现 IQueryable 的提供者提供的帽子数据结构可能不提供枚举能力。这取决于提供者。引用文档:“调用 Execute 方法时执行不返回可枚举结果的查询。” 也许我遗漏了一些明显的东西,但我仍然看不到“为什么我需要使用AsQueryable()?”的答案。如果我开始使用 IEnumerable(已经能够提供枚举功能的东西),为什么我需要使用扩展方法 AsQueryable() 转换为 IQueryable?也许一个小代码示例来说明这会派上用场?我似乎在你的解释中遗漏了一些东西。感谢您的宝贵时间。 @Ocelot20:要真正展示我需要编写查询提供程序的区别。你知道我们有linq to sqllinq to xml 等等.. 如果你想编写linq 驱动程序以你的 数据结构(linq to your data)为目标,那么你的 api 用户将拥有可以针对 your 数据结构运行 linq 查询,您必须选择 IQueryable 您似乎在解释IQueryableIEnumerable 之间的区别。我知道区别,这不是我要的。我在问为什么有人要采用实现IEnumerable 的东西,并使用AsQueryable 扩展方法将其转换为IQueryable。你是这么回答的吗?我还是说不出来..

以上是关于AsQueryable() 的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章

linq中AsEnumerable和AsQueryable的区别

为啥类 DBSet 的 AsQueryable() 不在 MSDN 文档中?

存储库中的异步 AsQueryable [重复]

C# groupby 使用 AsQueryable 报错

AsQueryable() 返回用户未处理的 NullReference 异常“对象引用未设置为对象实例”

Skip() 和 Take() asEnumerable 与 asQueryable