我有一个存储库类,它包含了我的LINQ to SQL数据上下文。存储库类是一个业务线类,包含所有数据层逻辑(以及缓存等)。
这是我的repo界面的v1。
public interface ILocationRepository
{
IList<Location> FindAll();
IList<Location> FindForState(State state);
IList<Location> FindForPostCode(string postCode);
}
但是为了处理FindAll的分页,我正在讨论是否要公开IQueryable&lt; ILocation&gt;而不是IList来简化诸如分页之类的情况的接口。
从数据仓库中公开IQueryable的优缺点是什么?
非常感谢任何帮助。
答案 0 :(得分:89)
专业人士;组合性:
缺点;非可测试性:
IQueryable<T>
可以组合,它会排除不可组合的实现 - 或者它会强制您为它们编写自己的查询提供程序为了稳定性,我采取了不在我的存储库中公开IQueryable<T>
或Expression<...>
。这意味着我知道存储库的行为方式,而我的上层可以使用模拟而不用担心“实际的存储库是否支持这个?” (强制进行集成测试)。
我仍然在存储库中使用IQueryable<T>
等 - 但不是在边界上。我发布了一些more thoughts on this theme here。将分页参数放在存储库接口上同样容易。您甚至可以使用扩展方法(在接口上)添加可选分页参数,这样具体类只有1个方法可以实现,但调用者可能有2或3个重载。< / p>
答案 1 :(得分: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则返回all ...
替代方式......
如果您仍想返回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
}
}
第一种方法优于内存,因为您将返回较少的数据,而不是全部。
答案 2 :(得分: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);
}
超级简洁。