返回IQueryable <T>或不返回IQueryable <T>

我有一个存储库类,包装我的LINQ到SQL数据上下文。 存储库类是包含所有数据层逻辑(以及caching等)的业务线类。

这是我的回购界面的v1。

public interface ILocationRepository { IList<Location> FindAll(); IList<Location> FindForState(State state); IList<Location> FindForPostCode(string postCode); } 

但是为了处理FindAll的分页问题,​​我在讨论是否公开IQueryable <ILocation>而不是IList来简化分页等情况的接口。

从数据仓库中暴露IQueryable的优点和缺点是什么?

任何帮助非常感谢。

优点; 可组合:

  • 来电者可以添加filter
  • 呼叫者可以添加分页
  • 来电者可以添加sorting
  • 等等

缺点 非可测性:

  • 您的存储库不再适合unit testing; 你不能依靠:它工作,B:它做什么 ;
    • 调用者可以添加一个不可翻译的函数(即没有TSQL映射;在运行时中断)
    • 调用者可以添加一个filter/sorting,使其performance得像一只狗
  • 由于调用者希望IQueryable<T>是可组合的,因此它排除了不可组合的实现 – 或者迫使您为他们编写自己的查询提供程序
  • 这意味着您无法优化/configurationDAL

为了稳定,我已经采取了暴露IQueryable<T>Expression<...>在我的资源库。 这意味着我知道存储库的行为,我的上层可以使用mock而不用担心“实际的存储库是否支持这个? (强制集成testing)。

我仍然在库中使用IQueryable<T>等,但不能跨越边界。 我在这里发表了更多关于这个主题的想法 。 在存储库接口上放置分页参数也很容易。 您甚至可以使用扩展方法(在接口上)添加可选的分页参数,以便具体类只有1个方法可以实现,但可能有2或3个重载可用于调用方。

正如前面的回答所提到的,暴露IQueryable使得调用者能够使用IQueryable本身,这或者会变得危险。

封装业务逻辑的首要职责是维护数据库的完整性。

你可以继续暴露IList,可能会改变你的参数如下,这是我们正在做…

 public interface ILocationRepository { IList<Location> FindAll(int start, int size); IList<Location> FindForState(State state, int start, int size); IList<Location> FindForPostCode(string postCode, int start, int size); } 

如果大小== -1,然后返回所有…

另一种方式…

如果你还想返回IQueryable,那么你可以在你的函数中返回List的IQueryable ..例如…

 public class MyRepository { IQueryable<Location> FindAll() { List<Location> myLocations = ....; return myLocations.AsQueryable<Location>; // here Query can only be applied on this // subset, not directly to the database } } 

第一种方法比内存有优势,因为你将返回更less的数据而不是全部。

我build议使用IEnumerable而不是IList ,因为它会有更多的灵活性。

通过这种方式,您将只能从数据库中获取您真正要使用的那部分数据,而无需在存储库中完成额外的工作。

样品:

 // Repository public interface IRepository { IEnumerable<Location> GetLocations(); } // Controller public ActionResult Locations(int? page) { return View(repository.GetLocations().AsPagination(page ?? 1, 10); } 

这是超级干净和简单。