73

我有一个将我的 LINQ to SQL Data Context 包装起来的存储库类。存储库类是包含所有数据层逻辑(以及缓存等)的业务线类。

这是我的 repo 界面的 v1。

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

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

从数据仓库中公开 IQueryable 的优缺点是什么?

很感谢任何形式的帮助。

4

3 回答 3

90

优点;可组合性:

  • 来电者可以添加过滤器
  • 来电者可以添加寻呼
  • 调用者可以添加排序
  • ETC

缺点;不可测试性:

  • 您的存储库不再是可正确进行单元测试的;你不能依赖a:它工作,b:它做什么
    • 调用者可以添加一个不可翻译的函数(即没有 TSQL 映射;在运行时中断)
    • 调用者可以添加一个过滤器/排序,使其像狗一样执行
  • 由于调用者希望IQueryable<T>是可组合的,因此它排除了不可组合的实现 - 或者它迫使您为他们编写自己的查询提供程序
  • 这意味着您无法优化/分析 DAL

为了稳定性,我已经采取公开IQueryable<T>或不公开Expression<...>我的存储库。这意味着我知道存储库的行为方式,并且我的上层可以使用模拟,而不必担心“实际的存储库是否支持这个?” (强制集成测试)。

我仍然在存储库中使用etcIQueryable<T> -但不是超出边界。我在这里发布了关于这个主题的更多想法。将分页参数放在存储库界面上同样容易。您甚至可以使用扩展方法(在接口上)添加可选的分页参数,这样具体类只有 1 个方法可以实现,但调用者可能有 2 或 3 个重载可用。

于 2009-04-05T09:20:10.650 回答
7

正如前面的答案所提到的,公开 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);
}

如果 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
    }
}

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

于 2009-04-05T09:20:28.480 回答
2

我建议使用IEnumerable而不是IList,使用它您将拥有更大的灵活性。

这样,您将能够从 Db 中仅获取您真正要使用的那部分数据,而无需在存储库中完成额外的工作。

样本:

// Repository
public interface IRepository
{
    IEnumerable<Location> GetLocations();
}

// Controller
public ActionResult Locations(int? page)
{
    return View(repository.GetLocations().AsPagination(page ?? 1, 10);
}

这是超级干净和简单。

于 2009-04-05T09:17:35.117 回答