Используйте IQueryable.Count <T> с параметром IEnumerable <T>
Представьте себе класс, допустим, для разбивки на страницы, который можно использовать с IList<T>
или IQueryable<T>
.
Этот класс будет иметь свойство int TotalItems
, которое (не удивительно) получит/задает счетчик запрашиваемого или перечислимого параметра.
Если я использую IEnumerable<T>
как параметр,
//simplified
public Pagination(IEnumerable<T> query)
{
TotalItems = query.Count();
}
метод Count()
будет (, если я не ошибаюсь, что точка) Enumerable.Count()
. Таким образом, даже если запрос является IQueryable<T>
(который наследуется от IEnumerable<T>
), он будет перечисляться (что явно не желательно с "запросом db" ).
Итак, есть способ использовать метод Queryable.Count()
, когда мой IEnumerable<T>
на самом деле является IQueryable<T>
, или мне нужно изменить свой дизайн, имея, например, в этом случае 2 ctor
//simplified
public Pagination(IEnumerable<T> query)
{
TotalItems = query.Count();
}
public Pagination(IQueryable<T> query)
{
TotalItems = query.Count();
}
ИЗМЕНИТЬ
Я понимаю, что IQueryable<T>
, наследующий от IEnumerable<T>
, не имеет ничего общего с тем, что IEnumerable<T>
и IQueryable<T>
имеют методы расширения с одинаковым именем, и что это хорошо, чтобы иметь одинаковые имена для метода расширения, которые выглядят они делают то же самое ", но я думаю, что это все еще иногда путается...
Общий вопрос для любопытства
Являются ли они другими примерами в рамках с той же "архитектурой": наследование + общие имена для методов расширения?
Ответы
Ответ 1
Вы должны иметь две перегрузки конструктора, как вы показали в вопросе. Это приводит к тому, что они хотят использовать методы IQueryable
или методы IEnumerable
, которые будут использоваться.
Если кто-то должен был сделать:
Pagination pager = new Pagination(query.AsEnumerable());
Затем они явно хотят, чтобы объект обрабатывался как IEnumearble
, а не IQueryable
. Возможно, они знают, что Skip
и Take
не выполняются поставщиком запросов, поэтому разбиение на страницы не будет выполнено, и его необходимо оценить как Linq-to-objects.
Имея две перегрузки, вы принимаете осознанное решение пользователя вашего класса о том, имеют ли они дело с последовательностью памяти или запросом, вместо того, чтобы пытаться выяснить это самостоятельно.
Ответ 2
Ну, вы могли бы сделать:
TotalItems = enumerable.AsQueryable().Count();
Это будет напрямую использовать реализацию query-provider Count
для собственного запроса или возврата к LINQ to Objects (с некоторыми служебными данными для использования EnumerableQuery
) в противном случае.
Другое решение (потенциально более эффективное):
var queryable = enumerable as IQueryable<T>;
TotalItems = queryable != null ? queryable.Count() : enumerable.Count();
Обратите внимание, что если ваш запрос реализует ICollection
или ICollection<T>
эффективно (как в случае с некоторыми поставщиками запросов), даже ваше существующее решение потенциально может работать хорошо из-за оптимизации в LINQ (он использует Count
свойство из этих интерфейсов, где это возможно). Для вашего IList<T>
он всегда будет использовать эту оптимизацию, так как она реализует ICollection<T>
.