设想一个课程,让我们说分页可以与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 query”显然不需要)。
当我的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>
具有相同名称的扩展方法这一事实无关,并且拥有相同的名称很好对于扩展方法,“看起来他们做同样的事情”,但我认为它仍然有时令人困惑......
好奇心的一般性问题
它们是否是框架中的其他示例,具有相同的“体系结构”:继承+扩展方法的通用名称?
答案 0 :(得分:8)
您应该有两个构造函数重载,正如您在问题中所示。这使得调用者可以使用IQueryable
方法,还是使用IEnumerable
方法。
如果有人这样做:
Pagination pager = new Pagination(query.AsEnumerable());
然后他们显然希望将对象作为IEnumearble
处理,而不是IQueryable
。也许他们知道Skip
和Take
没有被他们的查询提供者实现,因此分页将失败,并且需要将其评估为Linq-to-objects。
通过两次重载,您可以让您的班级用户知道他们是在处理内存序列还是查询,而不是试图自己解决这个问题。
答案 1 :(得分:1)
嗯,你可以这样做:
TotalItems = enumerable.AsQueryable().Count();
这将直接将查询提供程序的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>
。