【问题标题】:How Do SQL Transactions Work?SQL 事务如何工作?
【发布时间】:2009-11-03 15:46:16
【问题描述】:

我在 SQL 中工作的时间不长,但我认为我理解通过将 SQL 语句包装在事务中,所有语句都完成了,或者没有一个语句完成。这是我的问题。我有一个包含 lineitem 集合的订单对象。订单项与 order.OrderId 相关。我已验证 所有 ID 均已设置且正确,但是当我尝试保存(插入)订单时,我得到 INSERT 语句与 FOREIGN 冲突KEY 约束“FK_OrderItemDetail_Order”。冲突发生在数据库“MyData”、表“dbo.Order”、列“OrderId”中。

伪代码:

创建交易 交易开始() 插入订单 插入 order.LineItems

实际代码:

... 实体.Validate(); 如果(实体.IsValid) { SetChangedProperties(实体); entity.Install.NagsInstallHours = entity.TotalNagsHours; foreach (OrderItemDetail orderItemDetail in entity.OrderItemDetailCollection) { SetChangedOrderItemDetailProperties(orderItemDetail); } ValidateRequiredProperties(实体); TransactionManager transactionManager = DataRepository.Provider.CreateTransaction(); EntityState originalEntityState = entity.EntityState; 尝试 { entity.OrderVehicle.OrderId = entity.OrderId; entity.Install.OrderId = entity.OrderId; transactionManager.BeginTransaction(); SaveInsuranceInformation(transactionManager, entity); DataRepository.OrderProvider.Save(transactionManager, entity); DataRepository.OrderItemDetailProvider.Save(transactionManager, entity.OrderItemDetailCollection); if (!entity.OrderVehicle.IsEmpty) { DataRepository.OrderVehicleProvider.Save(transactionManager, entity.OrderVehicle); } 事务管理器.Commit(); } 抓住 { 如果(transactionManager.IsOpen) { transactionManager.Rollback(); } entity.EntityState = 原始实体状态; } } ...

有人建议我需要使用两笔交易,一笔用于订单,另一笔用于订单项,但我有理由确定这是错误的。但是我已经为此奋斗了一天多,我需要解决它,这样我才能继续前进,即使这意味着使用不好的解决方法。我可能只是在做一些愚蠢的事情吗?

【问题讨论】:

  • 看到上面的描述,您的订单ID似乎不是自动生成的。如果它是自动生成的,那么当您当时触发第一个 SQL 查询时,您将获得您需要在订单行中设置的订单 ID,以便第二个查询成功。
  • 你能发布完整的代码吗?在第一个 ADO.NET 中,没有“环境事务”,您需要将每个操作的事务对象引用作为参数传递。如果您错过了这一点,那么您描述的行为很常见。
  • 在一个事务中,任何事情都会发生或不发生,但操作仍然会一个接一个地发生。确保所有 SQL 命令都通过相同的事务。

标签: c# sql .nettiers


【解决方案1】:

我注意到您说您使用 NetTiers 进行代码生成。

我自己使用过 NetTiers,发现如果从表中删除外键约束,将其添加回同一个表,然后在对数据库进行更改后再次运行 NetTiers 的构建脚本可能有助于重置数据访问层。我曾尝试过这种方法,有时效果很好。

祝你的问题好运。

【讨论】:

  • 删除外键,保存表,添加外键并重新生成脚本。成功。在此之前我已经再生了十几次,希望它能解决问题,但什么也没有,所以我不认为它是 nettiers,但它现在正在工作,所以我是一个快乐的露营者。
【解决方案2】:

没有看到您的代码,很难说出问题所在。它可以是任何数量的东西,但看看这些:

  1. 这很明显,但是您的两个插入命令位于拥有该事务的同一连接上(并且该连接始终保持打开状态)对吗?
  2. 您是否在第一次插入后检索与约束相关的 ID,并在执行命令之前将其写回数据中以供第二次插入?
  3. 可能在 DB 中设置了错误的约束。

您绝对不想使用两个事务。

【讨论】:

  • 1.是的,没有地方可以从事务内部关闭连接。 2.我自己设置ID,在保存记录之前明确,并且我在插入订单后检查以确认ID没有被SQL更改。 3.不排除做错事,但约束无非是一个FK OrderItem.OrderId to PK Order.OrderId。
【解决方案3】:

看起来您的 lineItems 插入语句未正确设置订单的值。这应该是 Insert order 步骤的结果。您是否查看(并测试过)各个 SQL 语句?

我认为您的问题与事务控制无关。

【讨论】:

  • 我在插入之前已经按顺序设置了主键。在插入语句之后是相同的值。在尝试插入它们之前,所有订单项也已设置了相关的 orderid。
  • FK 约束检查的目的是什么?请补充一些信息,它可能指向另一个区域而不是表顺序的PK...
  • FK 是 PK Order.OrderId 的 OrderItem.OrderId 列。如果我保存并提交订单,然后尝试保存 OrderItems,它工作正常。
  • 嗯 .. 这听起来像是您实际上在为插入使用两个不同的事务。
【解决方案4】:

我没有这方面的经验,但看起来您可能指定了父表中不可用的键值。抱歉,除此之外我无法为您提供更多帮助。

【讨论】:

    【解决方案5】:

    问题在于您如何处理错误。发生错误时,事务不会自动回滚。您当然可以(并且可能应该)选择这样做,但根据您的应用程序或您所在的位置,您可能仍想提交它。在这种情况下,这正是你正在做的。您需要在那里包装一些错误处理代码,以便在发生错误时回滚您的代码。

    【讨论】:

    • 请理解我没有显示错误处理、回滚事务等。为了简洁起见,我试图只显示问题的摘要而不显示细节。如果事实证明需要详细信息,我将发布实际代码
    【解决方案6】:

    错误看起来像是没有为 LineItems 提供正确的 FK OrderId,这是通过将订单插入订单表自动生成的。您说您已经检查了 Id,您是否也检查了订单详细信息中的 FK?

    【讨论】:

    • 是的。我正在为 Id 使用 GUID,并且主键和外键都在事务打开之前设置,并且在插入语句之前和之后都已经检查过。
    • 然后我要检查的下一件事是确保错误消息中引用的外键约束实际上连接了正确的两个表......并且它朝着正确的方向前进. ...在右边的两列之间
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    • 2010-12-10
    • 2016-11-19
    • 1970-01-01
    相关资源
    最近更新 更多