将IQueryable.Count <t>与IEnumerable <t>参数</t> </t>一起使用

时间:2013-03-08 15:31:25

标签: c# linq ienumerable iqueryable

设想一个课程,让我们说分页可以与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>具有相同名称的扩展方法这一事实无关,并且拥有相同的名称很好对于扩展方法,“看起来他们做同样的事情”,但我认为它仍然有时令人困惑......

好奇心的一般性问题

它们是否是框架中的其他示例,具有相同的“体系结构”:继承+扩展方法的通用名称?

2 个答案:

答案 0 :(得分:8)

您应该有两个构造函数重载,正如您在问题中所示。这使得调用者可以使用IQueryable方法,还是使用IEnumerable方法。

如果有人这样做:

Pagination pager = new Pagination(query.AsEnumerable());

然后他们显然希望将对象作为IEnumearble处理,而不是IQueryable。也许他们知道SkipTake没有被他们的查询提供者实现,因此分页将失败,并且需要将其评估为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();

但是,请注意,如果您的可查询器有效地实现ICollectionICollection<T>(就像某些查询提供程序的情况一样),那么即使您现有的解决方案也可能因为LINQ中的优化而运行良好(它使用尽可能来自这些接口的Count属性。对于IList<T>,它将始终使用此优化,因为它实现了ICollection<T>