我的服务和存储层的职责
有一天我问了这个问题:
存储库层是否应该返回数据-transfer-objects (DTO)?
答案(好吧,只有一个人,但我已经有预感这不是一个好主意)是不,存储库稍后不应该处理DTO 对象(它们的目的纯粹是通过线路发送),服务层应该处理它。
现在我已经提出了一个结构,需要你的意见。这个想法是,当这样做有意义时,存储库层可以返回我定义的名为 IProjectable
的接口类型。这包装了查询(存储库层尚未执行查询),但不允许使用者更改查询(它不是 IQueryable
),只是对其执行投影操作(到目前为止对我来说)只有 First
和 ToPagedList
)将执行投影并实际执行查询。
所以在存储库中是这样的:
public IProjectable<User> GetUser(int id)
{
var query = from u in Set<User>()
where u.UserID == id
select u;
return query.AsProjectable();
}
在服务层中是这样的:
var dto = repository.GetUser(16).Single(u => new SimpleUserDto
{
FullName = u.FirstName + " " + u.LastName,
DisplayAddress = u.Address.Street + u.Address.HouseNumber,
OrderCount = u.Orders.Count()
});
return dto;
我是否正确地说,在这里进行实际的数据访问仍然是存储库层的责任(应该如此),并且投影到可序列化的层form 是服务层的责任(理应如此)?
我认为有效地执行此操作的唯一其他方法(从存储库返回 User
并对其 Orders 执行
会导致对数据库的额外查询)是定义一个具有所有这些属性的类型并从存储库层返回它,只是不要将其称为“Dto”,这看起来很愚蠢它与 DTO 相同,只是为了“纯粹”而没有命名相同。这样一来,我似乎就可以鱼与熊掌兼得了。Count()
服务层中的
我看到的缺点是,在服务层执行实际上无法转换为 SQL 的投影(它不应该)不必担心的情况下,或者在执行如此复杂的投影的情况下,您可能会遇到不匹配的情况这使得人们怀疑哪一层正在执行实际的数据访问。
顺便说一句,如果重要的话,我正在使用 Entity Framework 4。
The other day I asked this question:
Should the repository layer return data-transfer-objects (DTO)?
The answer (well by just one person, but I already had a hunch that it wasn't a good idea) was that no, the repository later should not have to deal with the DTO objects (their purpose is purely to be sent over the wire) and the service layer should deal with that.
Now I've come up with a construction in the meantime that I need your opinion on. The idea is that, when it makes sense to do so, the repository layer can return an interface type I've defined called IProjectable
. This wraps the query (the repository layer does not execute the query yet) but does not allow the consumer to change the query (it's not IQueryable
), just to perform projection operations on it (so far for me only First
and ToPagedList
) that would perform the projection and actually execute the query.
So something like this in the repository:
public IProjectable<User> GetUser(int id)
{
var query = from u in Set<User>()
where u.UserID == id
select u;
return query.AsProjectable();
}
And in the service layer something like this:
var dto = repository.GetUser(16).Single(u => new SimpleUserDto
{
FullName = u.FirstName + " " + u.LastName,
DisplayAddress = u.Address.Street + u.Address.HouseNumber,
OrderCount = u.Orders.Count()
});
return dto;
Am I correct in saying that doing the actual data access here is still the responsibility of the repository layer (as it should be) and that the projection to a serializable form is the responsibility of the service layer (as it should be)?
The only other way I see to do this efficiently (returning a User
from the repository and doing the Count()
on his Orders
in the service layer would result in an extra query to the database) is to define a type that has all these properties and return it from the repository layer and just don't call it "Dto", which seems silly as it would be identical to the DTO just not named the same for the sake of "purity". This way, it seems, I can have my cake and eat it for the most part too.
The downside I see is that you can get a mismatch where the service layer performs projections that can't actually be translated to SQL which it shouldn't have to worry about, or where it performs such complex projections that makes it questionable what layer is doing the actual data access.
I'm using Entity Framework 4 by the way, if it matters.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
是的,服务层仍然不知道实际的数据访问是如何执行的(因为它不应该不知道)。调用是否发送到 SQL?中间有缓存层吗?
对于这个问题,我使用管道模式,它基本上只是 IProjectable 上的一组扩展方法,可以执行经过测试的投影。接下来,在 serviceLayer 中,您可以使用这些管道方法的组合来编写查询,例如:
var users =repository.GetUsers().FilterByName("Polity").OrderByAge().ToTransferObjects();
Yes you are, the service layer still has no idea how the actual DataAccess is being performed (as it should not have not). Are the calls send to SQL? is there a caching layer in between?
For this problem i use a pipeline pattern which basicly is just a set of extension methods over IProjectable which can perform tested projections. Next, in your serviceLayer you can just write your query using a composition of these pipeline methods, for example:
var users = repository.GetUsers().FilterByName("Polity").OrderByAge().ToTransferObjects();
我最尊敬的一位开发人员 ayende (http://ayende.com/Blog/Default.aspx) 说:“ORM 是你的存储库”视频在这里 -> http ://community.devexpress.com/blogs/seth/archive/2011/03/09/interview-with-ayende-rahien-aka-oren-eini.aspx
问题是你真的需要存储库模式吗?
只是我的意见:)
One of developers I most respect ayende (http://ayende.com/Blog/Default.aspx) said: "ORM is your repository" video here -> http://community.devexpress.com/blogs/seth/archive/2011/03/09/interview-with-ayende-rahien-aka-oren-eini.aspx
Question is do you really need Repository pattern?
Just my opinion :)