AsQueryable()的用途是什么

本文关键字:是什么 AsQueryable | 更新日期: 2023-09-27 18:20:21

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的真实用例是什么?

AsQueryable()的用途是什么

有几个主要用途。

  1. 如其他答案中所述,您可以使用它来模拟使用内存中数据源的可查询数据源,这样您就可以更容易地测试最终将在基于不可枚举的IQueryable上使用的方法。

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

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

我对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()方法允许我在设置mock时满足编译器的要求。

不过,我对应用程序代码中使用这一点很感兴趣。

正如sanjuro所指出的,AsQueryable()的目的在将AsQueryable与Linq-To对象和Linq-To SQL一起使用中进行了解释。文章特别指出,

这在实体上的某些方法返回IQueryable of T,而某些方法返回List的真实场景中提供了极好的好处。但是,不管集合是以T的IQueryable还是T的IEnumerable返回,都需要对所有集合应用业务规则过滤器。从性能的角度来看,如果集合实现了IQueryable,那么您真的希望利用在数据库上执行业务过滤器,否则就可以使用Linq在内存中应用业务过滤器来实现委托。

本文使用带有Linq-To对象和Linq-To SQL 的AsQueryable对AsQueryable()的目的进行了详细解释

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

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

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

接口IQueryable引用文档:

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

因此,对于那些打算在.NET中使其数据处理可查询的人来说,不必要的数据结构可以被枚举或具有有效的枚举器

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