简体   繁体   English

使用IEnumerable <T> 和IQueryable <T> 在通用存储库中

[英]Using IEnumerable<T> and IQueryable<T> in a generic repository

I have read a number of posts regarding the implementation of a generic repository. 我已经阅读了一些关于通用存储库实现的帖子。 I have also read a number of posts that explain the difference between exposing IEnumerable vs. IQueryable from my repository. 我还阅读了一些帖子,解释了从我的存储库中暴露IEnumerable和IQueryable之间的区别。

I would like the flexibility of having my data filtered in the database (rather than in memory by the client) but want to avoid having to define a separate repository interface for all of my entities (and concrete classes that implement those interfaces). 我希望在数据库中过滤数据的灵活性(而不是客户端在内存中),但是要避免为我的所有实体(以及实现这些接口的具体类)定义单独的存储库接口。

So far my repository looks like this: 目前我的存储库看起来像这样:

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);
}

and an example of a concrete implementation is: 以及具体实现的一个例子是:

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);
    }
}

In this case my repository uses DbContext so what I would like to know is how this works with the generic interface: 在这种情况下,我的存储库使用DbContext所以我想知道的是它如何与通用接口一起工作:

  1. IQueryable<T> derives from IEnumerable<T> . IQueryable<T>派生自IEnumerable<T> In My find method I am returning an IQueryable<T> object but the client only sees this as an IEnumerable<T> . 在我的find方法中,我返回一个IQueryable<T>对象,但客户端只将其视为IEnumerable<T> Does this mean that if I carry out any subsequent queries on the IEnumerable<T> object it will actually perform the operations on the database and only return the results (because the object is a IQueryable in this case)? 这是否意味着如果我对IEnumerable<T>对象执行任何后续查询,它将实际执行数据库上的操作并仅返回结果 (因为在这种情况下对象 IQueryable )? Or, 要么,
  2. Only the Where clause that is passed into the Find method is executed on the database and any subsequent queries that are performed on the IEnumerable<T> object are performed on the client. 仅在数据库上执行传递给Find方法的Where子句,并且在客户端上执行在IEnumerable<T>对象上执行的任何后续查询。 Or, 要么,
  3. Neither of these happen and I have totally misunderstood how IEnumarable<T> , IQueryable<T> and Linq works. 这些都没有发生,我完全误解了IEnumarable<T>IQueryable<T>Linq工作的。

Update: 更新:

I am actually fairly surprised at the answers I have received in the comments. 实际上,我对评论中收到的答案感到非常惊讶。 My original repository returned IQueryable and subsequent research led me to believe this was a bad thing to do (eg if my viewModel accepts a repository in its constructor it can call any query it wants which makes it is more difficult to test). 我的原始存储库返回IQueryable并且随后的研究使我相信这是一件坏事(例如,如果我的viewModel在其构造函数中接受了一个存储库,它可以调用它想要的任何查询,这使得它更难以测试)。

All solutions I have seen for this so far involve create entity specific repositories so that IQueryable is not exposed (The only difference I guess is that I am doing this in a generic way). 到目前为止,我所看到的所有解决方案都涉及创建特定于实体的存储库,以便不暴露IQueryable(我猜的唯一区别是我以通用方式执行此操作)。

Because you are returning IEnumerable<T> all subsequent calls will be done in memory (locally). 因为您正在返回IEnumerable<T>所有后续调用都将在内存(本地)中完成。

One thing to keep in mind is that LINQ operates with a set of extension methods. 要记住的一件事是LINQ使用一组扩展方法。 There are extension methods for IQueryable<T> which support all of the necessary wiring to perform queries in locations other than locally, and extension methods for IEnumerable<T> that just work locally. IQueryable<T>有扩展方法,它支持在本地以外的位置执行查询所需的所有连接,以及IEnumerable<T>扩展方法,它们只在本地工作。

Note that which of these is selected is based on the compile time type, not the run time type. 请注意,选择了哪一个基于编译时类型,而不是运行时类型。 So an IQueryable that is cast to an IEnumerable will be treated as an IEnumerable . 因此, IQueryable转换为IEnumerableIQueryable将被视为IEnumerable This is different than the way class normally work (thanks to polymorphism) however it allows the call site control over how to perform these operations. 这与类通常工作的方式不同(多亏了多态)但是它允许调用站点控制如何执行这些操作。

An example of where this is useful is when you need to get all of the records in the table and then count them. 这有用的一个例子是当你需要获取表中的所有记录然后对它们进行计数时。 There is no need to perform the count in SQL if you are going to get all the results anyway. 如果要获得所有结果,则无需在SQL中执行计数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM