NHibernate LINQ查询性能,哪个代码片段更好?

3
这个问题在询问下面两种方法哪一种更受推荐(以及原因是什么)?
我正在使用C# 4.0在ServiceStack REST应用程序中使用FluentNHibernate,但这个问题通常适用于NHibernate LINQ查询。
哪种方法更受鼓励: (方法1) 快速运行一个简单的查询,返回所有与用户ID匹配的行:
// Query the User by id
var user = session.Get<User>(request.UserId);

然后可以单独使用LINQ在返回的List上进一步缩小结果范围:

// 'User' contains a 'List<Location>'
var locations = user.Locations.Where(location =>
                        location.Timestamp >= (request.MinTimestamp.HasValue ? request.MinTimestamp.Value : 0) &&
                        location.Timestamp <= (request.MaxTimestamp.HasValue ? request.MaxTimestamp.Value : DateTime.Now.ToTimestamp()));

return locations;

方法二,更鼓励运行一个更复杂的查询来在单个查询中完成上述操作:

var locationsQuery = session.QueryOver<LocationModel>()
                        .Where(table => table.User.Id == request.UserId)
                        .And(table => table.Timestamp >= (request.MinTimestamp.HasValue ? request.MinTimestamp.Value : 0))
                        .And(table => table.Timestamp <= (request.MaxTimestamp.HasValue ? request.MaxTimestamp.Value : DateTime.Now.ToTimestamp()));

return locationsQuery.List();

如果我的目标是:

a) 更快的执行时间


基准测试(已修订)

修订后的完整测试代码:http://pastebin.com/0ykKwcxX

基准测试输出:

方法15000次迭代中花费了147.291秒

最后一次迭代的查询结果:
{ Timestamp=1348659703485, Latitude=179.40000, Longitude=209.40000 }
{ Timestamp=1348659703486, Latitude=179.55000, Longitude=209.55000 }
{ Timestamp=1348659703487, Latitude=179.70000, Longitude=209.70000 }
{ Timestamp=1348659703488, Latitude=179.85000, Longitude=209.85000 }
{ Timestamp=1348659703489, Latitude=180.00000, Longitude=210.00000 }

方法25000次迭代中花费了133.728秒

最后一次迭代的查询结果:
{ Timestamp=1348659703485, Latitude=179.40000, Longitude=209.40000 }
{ Timestamp=1348659703486, Latitude=179.55000, Longitude=209.55000 }
{ Timestamp=1348659703487, Latitude=179.70000, Longitude=209.70000 }
{ Timestamp=1348659703488, Latitude=179.85000, Longitude=209.85000 }
{ Timestamp=1348659703489, Latitude=180.00000, Longitude=210.00000 }

差异:方法2大约快了13.5秒。


b) 长期重复使用和稳定性


1
我很惊讶第二种方法竟然比较慢。你有检查过NHibernate执行的SQL查询吗?能否请你把它们发出来? - Sebastian
2
在第一种情况下,您应该最终得到两个查询,例如:**select * from Users where UserId = <userid>select * from Locations where UserId = <userid>。在第二种情况下,我们只需要一个查询:select * from Locations where UserId = <userid>**。在您的测量中,第二个选项几乎慢了2倍 - 这让我感到惊讶。 - Sebastian
1
如果将DateTime.Now.Timestamp移出查询并存储在临时变量中,会发生什么?这是否会在数据库中被映射和重复执行?正如@lowleveldesign所说,看到NHibernate生成的原始SQL将是有趣的。 - Tim Croydon
1
var locationsQuery = ... 实际上并没有查询数据库。varIQueryOver<LocationModel> 而不是 List<LocationModel>。这些测试是否等效? - Andrew Whitaker
我确实遗漏了一个 locationsQuery.List<LocationModel>();,但这应该无关紧要,因为这会使代码变得更长,对吧? - Jason
显示剩余7条评论
1个回答

1

如果您的目标是更快的执行时间,那么我认为第二种方法最好,因为它不会对用户实体进行不必要的加载。话虽如此,我不是NHibernate的用户,所以不确定,如果这很重要,您应该进行一些经过测量的性能测试(“您无法改进您无法衡量的东西”-我记不清是谁说的,但这是一个相当不错的格言)。

Ayende写了很多值得一看的好的NHibernate文章(例如http://ayende.com/blog/3988/nhibernate-the-difference-between-get-load-and-querying-by-id

当涉及到“长期重用和稳定性”时,这有点取决于您如何使用此代码。您可以始终重构查询部分,使您的userid和timestamp过滤器成为扩展方法。请参阅此帖子以获取示例:http://lostechies.com/jimmybogard/2012/08/30/evolutionary-project-structure/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接