在 foreach 循环中回滚或提交事务
Rollback or commit Transactions inside a foreach loop
我在 MVC 应用程序中使用 Entity Framework 6 和 .Net Framework 4.8。我正在尝试对实体列表(发票)做两件事:
- 生成电子邮件并发送。
- 更新实体并保存。
当电子邮件发送失败时,我想回滚我对实体所做的所有更改。
这是我的代码:
foreach (var id in listOfIds)
{
using (var dbContextTransaction = db.Database.BeginTransaction())
{
try
{
var invoice = db.Invoices.Find(id);
MakeChangesToInvoice(invoice);
var pdf = GeneratePdf(invoice);
SendEmail(pdf);
db.SaveChanges();
dbContextTransaction.Commit();
}
catch(SomeEmailException)
{
dbContextTransaction.Rollback();
}
}
}
这里的问题是,当我在错误的迭代之后有一个成功的迭代时,错误迭代(称为回滚)的更改仍然会被保存。
您修改的实体仍由上下文跟踪,因此更改将在下一次 SaveChanges
调用(在下一次迭代中)传播到数据库,因此您需要重新创建您的 dbcontext(可能会对性能产生明显影响,需要检查)每次迭代或分离实体。像这样:
Invoice invoice = null;
try
{
invoice = db.Invoices.Find(id);
MakeChangesToInvoice(invoice);
var pdf = GeneratePdf(invoice);
SendEmail(pdf);
db.SaveChanges();
dbContextTransaction.Commit();
}
catch(SomeEmailException)
{
dbContextTransaction.Rollback();
// note that if you have complex object graph this possibly will not detach child objects
if(invoice != null) dbContext.Entry(invoice).State = EntityState.Detached;
}
或者如果合适的话使用this answer中的DetachAll
。另请注意,如果有两个对象,则重新创建上下文可能是更好的选择(或者您可以仅在前面 运行 中出现错误的情况下重新创建它)。
我在 MVC 应用程序中使用 Entity Framework 6 和 .Net Framework 4.8。我正在尝试对实体列表(发票)做两件事:
- 生成电子邮件并发送。
- 更新实体并保存。
当电子邮件发送失败时,我想回滚我对实体所做的所有更改。
这是我的代码:
foreach (var id in listOfIds)
{
using (var dbContextTransaction = db.Database.BeginTransaction())
{
try
{
var invoice = db.Invoices.Find(id);
MakeChangesToInvoice(invoice);
var pdf = GeneratePdf(invoice);
SendEmail(pdf);
db.SaveChanges();
dbContextTransaction.Commit();
}
catch(SomeEmailException)
{
dbContextTransaction.Rollback();
}
}
}
这里的问题是,当我在错误的迭代之后有一个成功的迭代时,错误迭代(称为回滚)的更改仍然会被保存。
您修改的实体仍由上下文跟踪,因此更改将在下一次 SaveChanges
调用(在下一次迭代中)传播到数据库,因此您需要重新创建您的 dbcontext(可能会对性能产生明显影响,需要检查)每次迭代或分离实体。像这样:
Invoice invoice = null;
try
{
invoice = db.Invoices.Find(id);
MakeChangesToInvoice(invoice);
var pdf = GeneratePdf(invoice);
SendEmail(pdf);
db.SaveChanges();
dbContextTransaction.Commit();
}
catch(SomeEmailException)
{
dbContextTransaction.Rollback();
// note that if you have complex object graph this possibly will not detach child objects
if(invoice != null) dbContext.Entry(invoice).State = EntityState.Detached;
}
或者如果合适的话使用this answer中的DetachAll
。另请注意,如果有两个对象,则重新创建上下文可能是更好的选择(或者您可以仅在前面 运行 中出现错误的情况下重新创建它)。