9

我已经阅读了许多关于实现通用存储库的帖子。我还阅读了许多帖子,这些帖子解释了从我的存储库中公开 IEnumerable 与 IQueryable 之间的区别。

我希望灵活地将我的数据过滤到数据库中(而不是由客户端在内存中过滤),但希望避免为我的所有实体(以及实现这些接口的具体类)定义单独的存储库接口。

到目前为止,我的存储库如下所示:

public interface IRepository<T>
{
    IEnumerable<T> GetAll();
    IEnumerable<T> Find(Expression<Func<T, bool>> where);

    void Add(T entity);
    void Attach(T entity);
    void Delete(T entity);
}

具体实现的一个例子是:

public class Repository<T> : IRepository<T> where T : class
{
    private DbContext _context;
    private DbSet<T> _entitySet;

    public Repository(DbContext context)
    {
        _context = context;
        _entitySet = _context.Set<T>();
    }

    public IEnumerable<T> GetAll()
    {
        return _entitySet;
    }

    public IEnumerable<T> Find(Expression<Func<T, bool>> where)
    {
        return _entitySet.Where(where);
    }

    public void Add(T entity)
    {
        _entitySet.Add(entity);
    }

    public void Attach(T entity)
    {
        _entitySet.Attach(entity);
    }

    public void Delete(T entity)
    {
        _entitySet.Remove(entity);
    }
}

在这种情况下,我的存储库使用DbContext所以我想知道的是它如何与通用接口一起使用:

  1. IQueryable<T>源自IEnumerable<T>。在我的 find 方法中,我返回一个IQueryable<T>对象,但客户端只将其视为IEnumerable<T>. 这是否意味着如果我对IEnumerable<T>对象执行任何后续查询,它实际上会在数据库上执行操作并且只返回结果(因为在这种情况下对象是 a IQueryable)?或者,
  2. 只有Where传入方法的子句在数据库上执行,对对象执行的Find任何后续查询都在客户端上执行。IEnumerable<T>或者,
  3. 这些都没有发生,我完全误解了它是如何IEnumarable<T>工作IQueryable<T>Linq

更新:

我实际上对我在评论中收到的答案感到相当惊讶。我的原始存储库返回了 IQueryable,随后的研究让我相信这是一件坏事(例如,如果我的 viewModel 在其构造函数中接受了一个存储库,它可以调用它想要的任何查询,这使得测试变得更加困难)。

到目前为止,我为此看到的所有解决方案都涉及创建特定于实体的存储库,以便不暴露 IQueryable(我猜唯一的区别是我以通用方式执行此操作)。

4

1 回答 1

6

因为您要返回IEnumerable<T>所有后续调用将在内存中(本地)完成。

要记住的一件事是 LINQ 使用一组扩展方法进行操作。有一些扩展方法IQueryable<T>支持在本地以外的位置执行查询所需的所有必要连接,而扩展方法IEnumerable<T>仅在本地工作。

请注意,选择哪一个是基于编译时类型,而不是运行时类型。因此IQueryable,转换为 an 的 anIEnumerable将被视为IEnumerable。这与类通常的工作方式不同(由于多态性),但是它允许调用站点控制如何执行这些操作。

这很有用的一个例子是当您需要获取表中的所有记录然后计算它们时。如果您要获得所有结果,则无需在 SQL 中执行计数。

于 2012-07-31T19:00:08.017 回答