What is the purpose of AsQueryable()?
Is the purpose of AsQueryable()
just so you can pass around an IEnumerable
to methods that might expect IQueryable
, or is there a useful reason to represent IEnumerable
as IQueryable
? For example, is it supposed to be for cases like this:
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();
}
Or does it actually make it do something different:
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();
Do the IQueryable
versions of these extension methods just build up an Expression that ends up doing the same thing once its executed? My main question is, whats a real use case for using AsQueryable
?
Solution 1:
There are a few main uses.
As mentioned in other answers, you can use it to mock a queryable data source using an in-memory data source so that you can more easily test methods that will eventually be used on a non-enumerable based
IQueryable
.You can write helper methods for manipulating collections that can apply to either in-memory sequences or external data sources. If you write your help methods to use
IQueryable
entirely you can just useAsQueryable
on all enumerables to use them. This allows you to avoid writing two separate versions of very generalized helper methods.It allows you to change the compile time type of a queryable to be an
IQueryable
, rather than some more derived type. In effect; you'd use it on anIQueryable
at the same times that you'd useAsEnumerable
on anIEnumerable
. You might have an object that implementsIQueryable
but that also has an instanceSelect
method. If that were the case, and you wanted to use the LINQSelect
method, you'd need to change the compile time type of the object toIQueryable
. You could just cast it, but by having anAsQueryable
method you can take advantage of type inference. This is simply more convenient if the generic argument list is complex, and it is actually necessary if any of the generic arguments are anonymous types.
Solution 2:
The most valid case I have for AsQueryable is unit testing. Say I have the following somewhat contrived example
public interface IWidgetRepository
{
IQueryable<Widget> Retrieve();
}
public class WidgetController
{
public IWidgetRepository WidgetRepository {get; set;}
public IQueryable<Widget> Get()
{
return WidgetRepository.Retrieve();
}
}
and I want to write a unit test to make sure the controller passes back the results returned from the repository. It'd look something like this:
[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));
}
where really, all the AsQueryable() method allows me to do is satisfy the compiler when setting up a mock.
I'd be interested where this is used in application code though.
Solution 3:
As sanjuro noted, the purpose of AsQueryable() is explained in Using AsQueryable With Linq To Objects And Linq To SQL. In particular, the article states,
This offers an excellent benefits in real word scenarios where you have certain methods on an entity that return an IQueryable of T and some methods return List. But then you have business rule filter that needs to be applied on all the collection regardless if the collection is returned as IQueryable of T or IEnumerable of T. From a performance stand point, you really want to leverage executing the business filter on the database if the collection implements IQueryable otherwise fall back to apply the business filter in memory using Linq to object implementation of delegates.