我有以下任务要做:计算所有有效帐户的利息。在过去,我使用Ado.Net和存储过程来做这样的事情。
这次我试图用NHibernate做到这一点,因为用纯POCO似乎可以更容易地完成复杂的算法。
所以我想做以下(伪代码):
foreach account in accounts
calculate interest
save account with new interest
我知道NHibernate不是为处理大数据量而设计的。对我来说,有可能组织这样一个循环,而不必同时在内存中拥有所有帐户。
为了最大限度地减少内存使用,我将IStatelessSession
用于外部循环而不是普通ISession
。
我尝试过Ayende提出的方法。有两个问题:
我的程序工作但是在打开Odbc跟踪之后我在调试器中看到所有提取都是在第一次执行.List
中的lambda表达式之前完成的。
我找到了另一种解决方案:session.Query
返回我在foreach中使用的.AsEnumerable()
。还有两个问题:
我不知道为什么,但IQueryOver
没有AsEnumerable
。它也没有List
方法与参数(如CreateQuery
)。我已经尝试了.Future
,但又一次:
总结:来自Ado.Net的NHibernate与dataReader.Read()
有任何等价物吗?
我对纯NHibernate方法的最佳替代方法是使用dataReader.Read()
进行主循环,然后使用来自Ado.Net循环的Load
帐户。但是性能会受到影响 - 通过密钥读取每个帐户比在外循环中完成的提取序列要慢。
我使用的是NHibernate 4.0.0.4000版。
答案 0 :(得分:1)
虽然NH的设计并未考虑到大型处理,但您始终可以通过应用层批处理来规避此限制。我发现,根据相关实体的对象图的大小,在将一定数量的对象加载到内存后,性能将受到影响(在一个小项目中,我可以加载100.000个对象,性能仍然可以接受,在另一个只有1500个对象,任何额外的Load()都会抓取)。
过去我使用分页处理批处理,当IStatelessSession结果集太差时(因为他们不加载代理等)。
因此,您在开始时进行计数查询,组成一些任意批量大小,然后开始对批处理进行处理。通过这种方式,您可以巧妙地避免n + 1选择问题,假设对于每个批处理,您明确地获取所需的所有内容。
需要注意的是,为了使其有效工作,您需要在完成后从ISession中逐出每个批处理的已处理实体。这意味着您必须在每个批次上提交事务。如果您可以使用多次刷新+提交,那么这可能适合您。
否则你将不得不去IStatelessSession尽管那里没有懒惰的查询。 "来自Books"意味着"从dbo.Books"中选择*或类似的东西,所有结果都被提取到内存中。