【发布时间】:2018-05-07 12:02:10
【问题描述】:
我有一个过程,我定期从数据库中检索记录,并对每个记录运行 3 个操作。对于每条记录,3 个操作必须要么全部成功,要么根本不成功。如果其中一个操作失败,我希望已经为之前的记录处理过的操作 已提交,因此下次进程运行时,它会获取之前 3 个事务之一失败的记录。
我想过将这 3 个操作包装在每条记录的事务中,并为每条记录循环,但我想确保在这种情况下使用数据库事务是有效的。以下是心中所想。对吗?
public async Task OrderCollectionProcessorWorker()
{
using (var context = new DbContext())
{
try
{
IList<Order> ordersToCollect =
await context.Orders.Where(
x => x.OrderStatusId == OrderStatusCodes.DeliveredId)
.ToListAsync(_cancellationTokenSource.Token);
await ProcessCollectionsAsync(context, ordersToCollect);
}
catch (Exception ex)
{
Log.Error("Exception in OrderCollectionProcessorWorker", ex);
}
}
}
/// <summary>
/// For each order to collect, perform 3 operations
/// </summary>
/// <param name="context">db context</param>
/// <param name="ordersToCollect">List of Orders for collection</param>
private async Task ProcessCollectionsAsync(DbContext context, IList<Order> ordersToCollect)
{
if (ordersToCollect.Count == 0) return;
Log.Debug($"ProcessCollections: processing {ordersToCollect.Count} orders");
foreach (var order in ordersToCollect)
{
// group the 3 operations in one transaction for each order
// so that if one operation fails, the operations performend on the previous orders
// are committed
using (var transaction = context.Database.BeginTransaction())
{
try
{
// *************************
// run the 3 operations here
// operations consist of updating the order itself, and other database updates
Operation1(order);
Operation2(order);
Operation3(order);
// *************************
await context.SaveChangesAsync();
transaction.Commit();
}
catch (Exception ex)
{
transaction?.Rollback();
Log.Error("General exception when executing ProcessCollectionsAsync on Order " + order.Id, ex);
throw new Exception("ProcessCollections failed on Order " + order.Id, ex);
}
}
}
}
【问题讨论】:
-
根据您的代码,似乎每个操作都在运行自己的事务(请参阅事务是在 foreach 循环中创建的)。如果想要实现所有都应该提交或不提交的事情,那么将事务创建移出 foreach 循环。
-
不,我想要一个事务中的所有 3 个操作,并为 IList 中的每个订单记录循环该操作。这 3 个操作将转到评论块所在的位置。
标签: c# .net database entity-framework transactions