我正在开发使用Asp.net MVC 4,NHibernate和Session-per-request。
我有一个更新多个数据库的服务方法,因此工作包含在TransactionScope中。我发现NHibernate Session在TransactionScope之外是不可用的,因为它不是线程安全的。
代码与此类似:
public void ProcessItems()
{
var items = itemService.GetAll();
var mailMessages = new List<MailMessage>();
using(var scope = new TransactionScope())
{
foreach(var item in items)
{
itemService.UpdateOne(item);
itemService.UpdateTwo(item);
try
{
mailMessages.Add(itemService.GenerateMailMessage(item));
}
catch(Exception ex)
{
// we don't want exceptions caused be generating email to prevent DB work
if (ex is InvalidOperationException
|| ex is NullReferenceException
|| ex is FormatException
|| ex is ArgumentException
|| ex is ItemNotFoundException)
{
LogError(String.Format("Unable to generate email alert for item.Id:{0} - {1}", item.Id, ex.Message), log);
}
else
{
// For exception types we don't know we can ignore rethrow
throw;
}
}
scope.Complete()
}
mailService.SendMail(mailMessages);
}
数据库更新对方法的成功至关重要。电子邮件提醒不是。我不希望生成电子邮件警报时出现问题,以防止发生数据库更新。
我的问题是:
修改
只是为了澄清我的问题:
我知道在TransactionScope之后生成并发送电子邮件会更好。但是我无法这样做,因为GenerateMailMessage()使用了在TransactionScope块之外使用不安全的NHibernate会话。
我想我真正想要的是将上面的catch语句更改为geniune catch-all(仍然使用日志记录)是否可以防御,以便为关键的UpdateOne()和UpdateTwo提供尽可能多的保护( )尽可能打电话?
答案 0 :(得分:3)
<强>更新强>
我的建议是尽量防止异常发生。如果做不到这一点,一个全能的可能是你剩下的唯一选择。记录所有异常在这里至关重要。
<小时/> 第一个问题:你的案例并不是真正的全部,你正在捕获查询类型的所有异常。我唯一的建议是记录您选择使用的例外的详细信息。
第二个问题:如果可能会失败,我会完全删除范围内的电子邮件。事务回滚后,所有项目也将回滚。在成功提交时创建并发送所有电子邮件。
public void ProcessItems()
{
var items = itemService.GetAll();
var mailMessages = new List<MailMessage>();
bool committed = false;
using(var scope = new TransactionScope())
{
foreach(var item in items)
{
itemService.UpdateOne(item);
itemService.UpdateTwo(item);
}
scope.Complete()
committed = true;
}
if (committed)
{
// Embed creation code and exception handling here.
mailService.SendMail(mailMessages);
}
}
答案 1 :(得分:0)
我建议改变这个。而不是在那里生成电子邮件然后...在本地列表中保留成功处理的项目列表,然后在提交后最后完成所有邮件发送。
public void ProcessItems()
{
var items = itemService.GetAll();
var successItems = new List<Item>();
var mailMessages = new List<MailMessage>();
using(var scope = new TransactionScope())
{
foreach(var item in items)
{
itemService.UpdateOne(item);
itemService.UpdateTwo(item);
successItems.Add(item);
// you still need try/catch handling for DB updates that fail... or maybe you want it all to fail.
}
scope.Complete()
}
mailMessages = successItems.Select(i => itemService.GenerateMailMessage).ToList();
//Do stuff with mail messages
}