在Web应用程序中使用NHibernate时,我通常会让我的IoC容器负责每个请求打开和关闭ISession
并提交/回滚事务。 HTTP的本质使得在这些应用程序中定义明确的工作单元变得非常容易。
现在,我的任务是整理一个小程序,该程序将由任务调度程序定期调用,以发送新闻通讯。新闻简报和订阅者的概念在我们的领域模型中已经是明确定义的实体,向所有订阅者发送简报将涉及做类似的事情:
var subscribers = _session
.QueryOver<Subscription>()
.Where(s => !s.HasReceivedNewsletter)
.List();
foreach (var subscriber in subscribers)
{
SendNewsletterTo(subscriber);
subscriber.HasReceivedNewsletter = true;
}
注意每个Subscriber
对象如何在循环中更新,记录她现在收到的简报。这个想法是,如果邮件发送程序崩溃,它可以重新启动并继续从它停止的地方发送新闻简报。
我面临的问题是在这里定义和实施工作单元模式。我可能需要在循环的每次迭代结束时提交对数据库的更改。简单地用using (var trans = _session.BeginTransaction())
块包裹循环体似乎在运行时非常昂贵,而且我似乎也经历了这个长时间运行的进程与使用相同数据库的其他(web)应用程序之间的锁定问题。
在阅读了有关NHibernate事务的一些文章和文档后,我开始思考,我可能需要从会话中分离订阅者列表以避免锁定问题,并将每个订阅者重新连接到循环体中的新会话。不过,我不确定这对性能有何影响。
那么,NHibernate专家,你会如何设计和实现这样一个长期运行的工作?
答案 0 :(得分:1)
你不想在这里使用异步持久消息吗?像NServiceBus,Rhino Service Bus或MassTransit之类的东西。看来你不必尽快发送大量的消息,所以我认为你应该以每个用户为基础的1个持久消息异步进行
答案 1 :(得分:0)
难道你不认为没有交易的无状态会话会在这里做得更好吗?
答案 2 :(得分:0)
会话中有多个事务没有问题。这里适合将事务范围限定为更新单个订户,因为它是一个独立的操作。根据订阅者数量和失败的可能性,最好一次抓住少量订阅者。
foreach (var subscriber in subscribers)
{
using (var txn = _session.BeginTransaction())
{
try
{
SendNewsletterTo(subscriber);
subscriber.HasReceivedNewsletter = true;
txn.Commit();
}
catch (Exception ex)
{
txn.Rollback();
// log exception, clean up any actions SendNewsletterTo has taken if needed
// Dispose of session and start over
}
}
}