Какова цель AsQueryable()?

Является ли цель 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

Существует несколько основных применений.

  • Как уже упоминалось в других ответах, вы можете использовать его для издевательства запрашиваемого источника данных с использованием источника данных в памяти, чтобы вы могли более легко тестировать методы, которые в конечном итоге будут использоваться на основе неперечислимых IQueryable.

  • Вы можете написать вспомогательные методы для управления коллекциями, которые могут применяться к последовательностям в памяти или внешним источникам данных. Если вы пишете свои методы помощи, чтобы использовать IQueryable целиком, вы можете просто использовать AsQueryable для всех перечислений, чтобы использовать их. Это позволяет избежать написания двух отдельных версий очень обобщенных вспомогательных методов.

  • Это позволяет вам изменить тип времени компиляции запрашиваемого как IQueryable, а не некоторый более производный тип. В результате; вы должны использовать его на IQueryable в то же время, что вы использовали бы AsEnumerable на IEnumerable. У вас может быть объект, реализующий IQueryable, но также имеющий метод экземпляра Select. Если это так, и вы хотите использовать метод LINQ Select, вам нужно будет изменить тип времени компиляции объекта на IQueryable. Вы можете просто бросить его, но, имея метод AsQueryable, вы можете использовать вывод типа. Это просто более удобно, если общий список аргументов является сложным, и это действительно необходимо, если какой-либо из общих аргументов является анонимными типами.

Ответ 2

Самый верный случай, который у меня есть для AsQueryable, - это модульное тестирование. Скажем, у меня есть следующий несколько надуманный пример

public interface IWidgetRepository
{
   IQueryable<Widget> Retrieve();
} 

public class WidgetController
{
   public IWidgetRepository WidgetRepository {get; set;}


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

и я хочу написать unit test, чтобы убедиться, что контроллер передает результаты, возвращенные из репозитория. Это выглядит примерно так:

[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

Как отметил санджуро, цель AsQueryable() объясняется в Использование AsQueryable с Linq To Objects и Linq To SQL. В частности, статья гласит:

Это дает отличные преимущества в сценариях реального слова, где у вас есть определенные методы для объекта, возвращающего IQueryable из T, а некоторые методы возвращают List. Но тогда у вас есть фильтр бизнес-правил, который необходимо применять во всей коллекции независимо от того, возвращается ли коллекция как IQueryable из T или IEnumerable из T. С точки зрения производительности вы действительно хотите использовать выполнение бизнес-фильтра в базе данных, если коллекция реализует IQueryable, в противном случае отпадает, чтобы применить бизнес-фильтр в памяти с помощью Linq для объектной реализации делегатов.

Ответ 4

Интерфейс IQueryable цитирование документации:

Интерфейс IQueryable предназначен для реализации по запросу провайдеров.

Итак, для кого-то, кто намеревается сделать его datastracture запрошенным в .NET, эта структура данных, которая не нужна, может быть перечислина или иметь действительный счетчик.

IEnumerator - это интерфейс для итерации и обработки потока данных.

Ответ 5

Цель AsQueryable() объясняется в этой статье Использование AsQueryable с Linq для объектов и Linq To SQL

Из раздела Замечания MSDN Queryable.AsQueryable Method:

Если тип источника реализует IQueryable, AsQueryable (IEnumerable) возвращает его напрямую. В противном случае он возвращает IQueryable, который выполняет запросы, вызывая эквивалентные методы оператора запроса в Enumerable, а не в Queryable.

Это именно то, что упоминается и используется в этой статье. В вашем примере это зависит от того, что будет orderRepo.GetAll, IEnumerable или IQueryable (Linq to Sql). Если он возвращает IQueryable, метод Count() будет выполняться в базе данных, иначе он будет выполнен в памяти. Посмотрите внимательно на пример в указанной статье.