简体   繁体   English

Entity Framework Core - 提交后事务不能回滚

[英]Entity Framework Core - transaction cannot be roll back after commit

It is using Entity Framework Core to update database.它使用 Entity Framework Core 来更新数据库。

dbContextTransaction.Commit(); is working fine, and after this it is some file operation with bad result.工作正常,之后是一些文件操作,结果不好。 And then it throws an error, so it tries to roll back using dbContextTransaction.Rollback();然后它抛出一个错误,所以它尝试使用dbContextTransaction.Rollback();回滚but results in:但导致:

This SqlTransaction has completed;此 SqlTransaction 已完成; it is no longer usable.它不再可用。

DbContext dbContext = scope.ServiceProvider.GetService<DbContext>();
IDbContextTransaction dbContextTransaction = dbContext.Database.BeginTransaction();

try
{
    IOrderDao orderDao = scope.ServiceProvider.GetService<IOrderDao>();
    IItemSoldPriceDao itemSoldPriceDao = scope.ServiceProvider.GetService<IItemSoldPriceDao>();

    ItemSoldPrice itemSoldPrice = new ItemSoldPrice
    {
      ...
    };

    itemSoldPriceDao.AddItemSoldPrice(itemSoldPrice);
    order.SoldPriceCaptured = true;

    dbContext.SaveChanges();
    dbContextTransaction.Commit();
    //... some other file operation throws out error
    throw new Exception("aaa");
}
catch (Exception ex)
{
    CommonLog.Error("CaptureSoldPrice", ex);
    dbContextTransaction.Rollback();
}

After a transaction is committed, it cannot be rolled back?事务提交后就不能回滚?

When using Entity Framework, explicit transactions are only required when you want to link the success or failure of operations against the DbContext with other operations outside of the scope of the DbContext.使用实体框架时,仅当您希望将对 DbContext 的操作的成功或失败与 DbContext 的 scope之外的其他操作联系起来时,才需要显式事务。 All operations within a DbContext prior to SaveChanges are already grouped within a transaction. SaveChanges之前 DbContext 中的所有操作都已在事务中分组。 So for instance saving entities across two or more tables within a DbContext do not require setting up an explicit transaction, they will both be committed or rolled back together if EF cannot save one or the other.因此,例如在 DbContext 中跨两个或多个表保存实体不需要设置显式事务,如果 EF 无法保存其中一个或另一个,它们将一起提交或回滚。

When using an explicit transaction, the Commit() call should be the last operation for what forms essentially a unit of work.当使用显式事务时, Commit()调用应该是 forms 本质上是一个工作单元的最后一个操作。 It will be the last operation to determine whether everything in the transaction scope is successful or not.这将是确定事务 scope 中的所有内容是否成功的最后一个操作。 So as a general rule, all operations, whether Database-based, file based, or such should register with and listen to the success or failure of the transaction.因此,作为一般规则,所有操作,无论是基于数据库的、基于文件的还是其他操作,都应该注册并监听事务的成功或失败。

An example of using a transaction: Say we have a system that accesses two databases via two separate DbContexts.使用事务的示例:假设我们有一个系统通过两个单独的 DbContext 访问两个数据库。 One is an order system that tracks orders and has a record for a Customer and one is a CRM that tracks customer information.一种是跟踪订单并记录客户的订单系统,另一种是跟踪客户信息的 CRM。 When we accept a new order from a new customer we check the CRM system for a customer and create a customer record in both systems if it is someone new.当我们接受来自新客户的新订单时,我们会检查客户的 CRM 系统并在两个系统中创建客户记录(如果是新客户)。

using (var orderContext = new OrderDbContext())
{
    var transaction = orderContext.Database.BeginTransaction();

    try
    {
        var order = new Order
        {
            // TODO: Populate order details..
        }

        if(customerDetails == null && registerNewCustomer) // Where customerDetails = details loaded if an existing customer logged in and authenticated...
        {
            using(var crmContext = new CrmDbContext())
            {
                crmContext.Database.UseTransaction(transaction);
                var customer = new Customer 
                {
                    UserName = orderDetails.CustomerEmailAddress,
                    FirstName = orderDetails.CustomerFirstName,
                    LastName = orderDetails.CustomerLastName,
                    Address = orderDetails.BillingAddress
                };
                crmContext.Customers.Add(customer);
                crmContext.SaveChanges();
                var orderCustomer = new Orders.Customer
                {
                    CustomerId = customer.CustomerId,
                    FirstName = customer.FirstName,
                    LastName = customer.LastName
                }
                orderContext.Customers.Add(orderCustomer);
            }
         }
         
         order.CustomerId = crmContext.Customers
             .Select(c => c.CustomerId)
             .Single(c => c.UserName == customerDetails.UserName);
     
         orderContext.Orders.Add(order);
         orderContext.SaveChanges();

         transaction.Commit();
     }
     catch(Exception ex)
     {
         // TODO: Log exception....         
         transaction.Rollback();
     }
}

The order DB customer is just a thin wrapper of the CRM customer where we would go for all of the customer details.订单数据库客户只是 CRM 客户的一个薄包装,我们将在其中 go 获取所有客户详细信息。 The CRM customer manages the Customer IDs which would correlate to an Order Customer record. CRM 客户管理与订单客户记录相关的客户 ID。 This is by no means a production code type example, but rather just to outline how a Transaction might coordinate multiple operations when used.这绝不是一个生产代码类型的示例,而只是概述一个事务在使用时如何协调多个操作。

In this way if there is any exception raised at any point, such as after a new Customer record has been created and saved, all saved changes will be rolled back and we can inspect any logged exception details along with recorded values to determine what went wrong.这样,如果在任何时候出现任何异常,例如在创建和保存新的客户记录之后,所有保存的更改都将回滚,我们可以检查任何记录的异常详细信息以及记录的值以确定出了什么问题.

When dealing with combinations of DbContext operations and other operations that we might want to support a rolling back process flow on failure then we can leverage constructs like the TransactionScope wrapper.在处理 DbContext 操作和其他操作的组合时,我们可能希望在失败时支持回滚流程流,然后我们可以利用TransactionScope包装器之类的构造。 However this should be used with caution and only in cases where you explicitly need to marry these operations rather than attempting to use the pattern as a standard across all operations.但是,这应该谨慎使用,并且仅在您明确需要结合这些操作而不是尝试将模式用作所有操作的标准的情况下使用。 In most cases you will not need explicit transactions.在大多数情况下,您不需要显式事务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM