存储库是否应将IQueryable暴露给服务层或在实现中执行过滤?

mat*_*att 11 linq-to-entities iqueryable repository-pattern linq-to-sql

我正在尝试在我的MVC应用程序中决定数据访问的最佳模式.目前,在遵循MVC店面系列之后,我正在使用存储库,将IQueryable暴露给服务层,然后服务层应用过滤器.最初我一直在使用LINQtoSQL

public interface IMyRepository
{
  IQueryable<MyClass> GetAll();
}
Run Code Online (Sandbox Code Playgroud)

实施于:

public class LINQtoSQLRepository : IMyRepository
{
   public IQueryable<MyClass> GetAll()
   {
      return from table in dbContext.table
             select new MyClass
             {
                Field1 = table.field1,
                ... etc.
             }
   }
}
Run Code Online (Sandbox Code Playgroud)

过滤ID:

public static class TableFilters 
{       
   public static MyClass WithID(this IQueryable<MyClass> qry, string id)
   {
      return (from t in qry
              where t.ID == id
              select t).SingleOrDefault();
   }     
}
Run Code Online (Sandbox Code Playgroud)

从服务中调用:

public class TableService
{
   public MyClass RecordsByID(string id)
   {
      return _repository.GetAll()
                        .WithID(id);
   }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试使用带有LINQ to Entities的Entity Framework实现存储库时,我遇到了一个问题.我的项目中的过滤器类包含一些比上面示例中的"WHERE ... == ..."更复杂的操作,我认为这需要不同的实现,具体取决于LINQ提供程序.具体来说,我需要执行SQL"WHERE ... IN ..."子句.我能够在过滤器类中使用以下方法实现:

string[] aParams = // array of IDs
qry = qry.Where(t => aParams.Contains(t.ID));
Run Code Online (Sandbox Code Playgroud)

但是,为了针对Entity Framework执行此操作,我需要提供一个解决方案,例如与实体框架绑定的BuildContainsExpression.这意味着我必须具有此特定过滤器的2种不同实现,具体取决于底层提供程序.

我很感激有关如何从这里开始的任何建议.在我看来,从我的存储库中公开IQueryable将允许我对其执行过滤器而不管底层提供者,使我能够在需要时在提供者之间切换.但是我上面描述的问题让我觉得我应该在存储库中执行所有过滤并返回IEnumerable,IList或单个类.

非常感谢,马特

mik*_*igs 14

这是一个非常受欢迎的问题.一个我不断问自己的问题.我一直认为最好从存储库返回IEnumerable而不是IQueryable.

存储库的目的是封装数据库基础结构,以便客户端不必担心数据源.但是,如果您返回IQueryable,那么您将受到消费者的支配,因为对于您的数据库将运行什么类型的查询,以及他们是否会执行LINQ提供程序不支持的操作.

以分页为例.假设您有一个Customer实体,您的数据库可能有数十万客户.您希望您的客户编写哪些代码?

var customers = repos.GetCustomers().Skip(skipCount).Take(pageSize).ToList();
Run Code Online (Sandbox Code Playgroud)

要么

var customers = repos.GetCustomers(pageIndex, pageSize);
Run Code Online (Sandbox Code Playgroud)

在第一种方法中,您使存储库无法限制从数据源检索的记录数.此外,您的消费者必须计算skipCount.

在第二种方法中,您为客户端提供了更粗糙的接口.现在,您的存储库可以对pageSize强制执行一些约束,以优化查询.您还封装了skipCount的计算.

但是,话说回来,在您的情况下,您的客户就是您的服务.所以我认为问题实际上归结为关注点的分离.在哪里执行此类验证逻辑会更好?那么答案很可能是"在服务中".但是"包含查询逻辑在哪里更好?"的答案呢?对我来说答案显然是"存储库".这是其预期的专业领域.