存储库是否应该将 IQueryable 暴露给服务层或在实现中执行过滤?
我正在尝试确定 MVC 应用程序中数据访问的最佳模式。 目前,在遵循 MVC 店面系列之后,我正在使用存储库,将 IQueryable 暴露给服务层,然后应用过滤器。最初我一直在使用 LINQtoSQL,例如
public interface IMyRepository
{
IQueryable<MyClass> GetAll();
}
实现于:
public class LINQtoSQLRepository : IMyRepository
{
public IQueryable<MyClass> GetAll()
{
return from table in dbContext.table
select new MyClass
{
Field1 = table.field1,
... etc.
}
}
}
过滤 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();
}
}
从服务调用:
public class TableService
{
public MyClass RecordsByID(string id)
{
return _repository.GetAll()
.WithID(id);
}
}
当我尝试使用带有 LINQ to Entities 的实体框架来实现存储库时,我遇到了问题。我的项目中的过滤器类包含一些比上面示例中的“WHERE ... == ...”更复杂的操作,我认为这需要不同的实现,具体取决于 LINQ 提供程序。具体来说,我需要执行 SQL“WHERE ... IN ...”子句。我可以使用以下方法在过滤器类中实现此操作:
string[] aParams = // array of IDs
qry = qry.Where(t => aParams.Contains(t.ID));
但是,为了针对实体框架执行此操作,我需要提供一个解决方案,例如 BuildContainsExpression 与实体框架绑定。这意味着我必须有这个特定过滤器的两种不同实现,具体取决于底层提供程序。
对于我应该如何从这里继续进行的任何建议,我将不胜感激。 在我看来,从我的存储库中公开 IQueryable 将使我能够对其执行过滤器,而不管底层提供程序如何,从而使我能够在需要时在提供程序之间进行切换。然而,我上面描述的问题让我认为我应该在存储库中执行所有过滤并返回 IEnumerable、IList 或单个类。
非常感谢, 马特
I'm trying to decide on the best pattern for data access in my MVC application.
Currently, having followed the MVC storefront series, I am using repositories, exposing IQueryable to a service layer, which then applies filters. Initially I have been using LINQtoSQL e.g.
public interface IMyRepository
{
IQueryable<MyClass> GetAll();
}
Implemented in:
public class LINQtoSQLRepository : IMyRepository
{
public IQueryable<MyClass> GetAll()
{
return from table in dbContext.table
select new MyClass
{
Field1 = table.field1,
... etc.
}
}
}
Filter for IDs:
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();
}
}
Called from service:
public class TableService
{
public MyClass RecordsByID(string id)
{
return _repository.GetAll()
.WithID(id);
}
}
I ran into a problem when I experimented with implementing the repository using Entity Framework with LINQ to Entities. The filters class in my project contains some more complex operations than the "WHERE ... == ..." in the example above, which I believe require different implementations depending on the LINQ provider. Specifically I have a requirement to perform a SQL "WHERE ... IN ..." clause. I am able to implement this in the filter class using:
string[] aParams = // array of IDs
qry = qry.Where(t => aParams.Contains(t.ID));
However, in order to perform this against Entity Framework, I need to provide a solution such as the BuildContainsExpression which is tied to the Entity Framework. This means I have to have 2 different implementations of this particular filter, depending on the underlying provider.
I'd appreciate any advice on how I should proceed from here.
It seemed to me that exposing an IQueryable from my repository, would allow me to perform filters on it regardless of the underlying provider, enabling me to switch between providers if and when required. However the problem I describe above makes me think I should be performing all my filtering within the repositories and returning IEnumerable, IList or single classes.
Many thanks,
Matt
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是一个非常受欢迎的问题。这是我不断问自己的一个问题。我一直认为最好从存储库返回 IEnumerable 而不是 IQueryable。
存储库的目的是封装数据库基础设施,因此客户端无需担心数据源。但是,如果您返回 IQueryable,那么您将受到消费者的支配,决定将对您的数据库运行哪种查询,以及它们是否会执行 LINQ 提供程序不支持的操作。
以分页为例。假设您有一个客户实体,并且您的数据库可能有数十万个客户。您希望您的客户编写哪些代码?
或者
在第一种方法中,您使存储库无法限制从数据源检索的记录数量。此外,您的消费者必须计算skipCount。
在第二种方法中,您向客户端提供更粗粒度的界面。现在,您的存储库可以对 pageSize 实施一些约束,以优化查询。您还封装了skipCount 的计算。
然而,话虽如此,在您的情况下,您的客户就是您的服务。所以我认为这个问题实际上归结为关注点分离。在哪里执行这样的验证逻辑更好?那么这个答案很可能是“在服务中”。但是“最好在哪里包含查询逻辑?”的答案又如何呢?对我来说,答案显然是“存储库”。这是其预期的专业领域。
This is a very popular question. One that I constantly ask myself. I've always felt it best to return IEnumerable rather than IQueryable from a repository.
The purpose of a repository is to encapsulate the database infrastructure so the client need not worry about the data source. However, if you return IQueryable you are at the mercy of the consumer as to what kind of query will get run against your db, and whether they will do something that the LINQ provider doesn't support.
Take paging for example. Lets say you have a Customer entity and your database could have hundreds of thousands of customers. Which code would you rather have your client write?
OR
In the first approach you make it impossible for the repository to restrict the number of records retrieved from the data source. Also, your consumer has to calculate the skipCount.
In the second approach you provide a more coarse grained interface to your client. Now your repository can enforce some constraints on the pageSize in order to optimize the query. You also encapsulate the calculation of the skipCount.
However, that being said, in your situation your client is your service. So I suppose the question really comes down to a separation of concerns. Where is it better to perform such validation logic? Well that answer may very well be "in the service". But what about the answer to "Where is it better to contain query logic?". To me the answer is clearly "The Repository". That is its intended area of expertise.