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

107

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

Оцелот20
источник

Ответы:

66

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

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

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

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

Сервировка
источник
Спасибо за возрождение. Отметьте это как ответ, поскольку он является наиболее полным.
Ocelot20
5
Учитывая то , что IQueryable<T>происходит от IEnumerable<T>Пожалуйста , вы можете объяснить , что вы имеете в виду под «не-перечислимой основой IQueryable»?
Дай
2
@Dai Это относится к файлу, IQueryableкоторый представляет собой нечто большее, чем просто обертку IEnumerable, и на самом деле использует дополнительные возможности IQueryable.
Servy
№1 - это именно то, что я искал. Спасибо за проверку.
one.beat.consumer
12

Самый верный случай для 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 (), - это удовлетворять компилятор при настройке макета.

Мне было бы интересно, где это используется в коде приложения.

Крис
источник
11

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

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

Скотт
источник
6

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

Из раздела «Примечания» метода MSDN Queryable.AsQueryable:

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

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

сандзюро
источник
3

IQueryableЦитата из документации по интерфейсу :

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

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

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

Тигран
источник
Не могли бы вы перефразировать это: «та структура данных, в которой нет необходимости, может быть перечислена или иметь действительный перечислитель». Я понимаю разницу между IQueryableи IEnumerable, но не понимаю варианта использования AsQueryableметода расширения. Я немного запутался в этом предложении, которое, как я подозреваю, может иметь ответ, который я ищу (на основе голосов).
Ocelot20
@ Ocelot20: это означает, что структура данных, представленная поставщиком, который реализует IQueryable, может не обеспечивать возможности перечисления. Это зависит от провайдера. Цитата из документа: «Запросы, не возвращающие перечислимых результатов, выполняются при вызове метода Execute».
Тигран
Может быть, мне не хватает чего-то очевидного, но я все еще не вижу ответа на вопрос «зачем мне это использовать AsQueryable()?» Если я начиная с IEnumerable (что - то уже способным создавать возможность перечисления), то почему бы мне нужно преобразовать , чтобы с IQueryableпомощью метода расширения AsQueryable()? Может быть, небольшой пример кода, чтобы проиллюстрировать, где это может пригодиться? Кажется, я что-то упускаю в вашем объяснении. Спасибо за ваше время.
Ocelot20
@ Ocelot20: чтобы действительно показать разницу, мне нужно написать поставщика запросов. Вы знаете, что у нас есть linq to sql, linq to xmlи поэтому ... если вы хотите написать linqдрайвер, нацеленный на ваши структуры данных ( linq to your data), чтобы пользователи вашего api имели возможность запускать linqзапросы к вашим структурам данных, вы должны выбратьIQueryable
Тигран,
2
Кажется, вы объясняете разницу между IQueryableи IEnumerable. Я знаю разницу, и я не об этом прошу. Я спрашиваю , почему кто - то хотел взять что - то , что орудия IEnumerable, и превратить его в IQueryableиспользовании AsQueryableметод расширения. Это то, что вы отвечаете? Я все еще не могу сказать ..
Ocelot20