要返回IQueryable <t>或不返回IQueryable <t> </t> </t>

时间:2009-04-05 09:15:58

标签: c# .net linq-to-sql iqueryable

我有一个存储库类,它包含了我的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的优缺点是什么?

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:89)

专业人士;组合性:

  • 来电者可以添加过滤器
  • 来电者可以添加分页
  • 来电者可以添加排序

缺点;非可测试性:

  • 您的存储库不再适用于单元可测试;你不能依靠:它工作,b:它做什么;
    • 调用者可以添加不可翻译的函数(即没有TSQL映射;在运行时中断)
    • 调用者可以添加一个过滤器/排序,使其像狗一样执行
  • 由于调用者希望IQueryable<T>可以组合,它会排除不可组合的实现 - 或者它会强制您为它们编写自己的查询提供程序
  • 表示您无法优化/配置DAL

为了稳定性,我采取了在我的存储库中公开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);
}

超级简洁。