【问题标题】:Cannot swap unique value on two rows with EF无法使用 EF 交换两行的唯一值
【发布时间】:2015-09-18 01:11:15
【问题描述】:

我正在尝试将唯一列中的值交换为两行(或更多行)。例如:

更新前:

  • 第 1 行 = 1
  • 第 2 行 = 2

更新后:

  • 第 1 行 = 2
  • 第 2 行 = 1

我正在使用实体框架。这些更改发生在同一上下文的单个提交上。但是,当我尝试此更新时,总是会遇到唯一的约束冲突。 EF 没有使用事务吗?

为了完整起见,这里是我的表格设计的 sn-p:

[FeeSchemeId] UNIQUEIDENTIFIER NOT NULL,
[SortPosition] INT NOT NULL,
UNIQUE (FeeSchemeId, SortPosition)

我正在尝试更新“SortPosition”列。此处显示的代码有点复杂,但我可以向您保证,它是与单个最终提交相同的上下文。该错误仅在 EF 尝试写入数据库时​​引发。

更新:

- 使用 SQL Server Profiler,我可以看到 EF 正在为每个受影响的行运行单独的 UPDATE。 EF 不应该对 SaveChanges() 的一次调用使用单个事务吗? -

更新 2:

事实证明 EF 毕竟使用的是单个事务。 SQL Profiler 正在过滤掉它。

【问题讨论】:

  • "EF 没有使用事务吗?" - 这取决于您正在运行的查询类型 (see this article)。假设它是一个 UPDATE 查询(应该是),它应该将它包装在一个事务中。
  • 您可能必须使其中一个键为空,或者删除并重新插入行(假设这只是一个连接表)。
  • 根据 SQL 分析器。 EF 正在运行两个单独的语句来更新这两行。
  • 你能展示一个精简版的 LINQ 查询吗?另外,即使执行了“两条单独的语句”,事务也在连接上,所以当异常发生时,这两个查询会一起回滚。
  • @TiesonT。我可以看到它通过使用类似的操作运行单独的语句,该操作不会对 UNIQUE 列进行任何更改。

标签: c# sql-server entity-framework unique-constraint


【解决方案1】:

你不能用 2 条语句也用 SQL Server 来做到这一点。您需要使用第三个值

BEGIN TRANSACTION;
UPDATE MyTable Set Id = 200 where Id = 1;
UPDATE MyTable Set Id = 1 where Id = 2;
UPDATE MyTable Set Id = 2 where Id = 200;
COMMIT;

顺便说一句,SQL Server 分析器显示 BEGIN TRANSACTION/COMMIT 语句

【讨论】:

  • SQL Server Profiler 仅显式包含 TRANSACTION 语句。默认情况下不包含它们。
  • 是的,你是对的。在您的情况下,如果 EF 在显式事务中生成 2 个更新语句,那么您可以预期它会起作用,因此 SQL Server Profiler 应该显示它。而且在同一个事务里面SQL Server也不行……
  • 看来你是对的。我预计,因为所有更改都在事务期间完成,SQL Server 将根据整体效果而不是单个语句检查约束。
  • SQL 不会在事务期间禁用约束...这是一个常见的误解。见这里stackoverflow.com/questions/5974731/…
  • 我不认为这是真的,我可以用 TSQL 中的一条语句交换两个唯一值。 (好吧,也许你不能用 2 个语句来做,因为中间的东西是无效的,但你可以用 1 个语句来做。)
【解决方案2】:

我使用的另一种不依赖临时值(它们本身就有可能违反唯一性)的技巧是发出单个 UPDATE,如下所示:

UPDATE MyTable
SET ID = case when id = 1 then 2 else 1 end
WHERE ID in (1, 2)

不幸的是,EF 不够聪明,无法自行生成这些类型的语句。

【讨论】:

  • 是的,这行得通,而且您似乎无法通过 EF 做到这一点。问题是,如果您正在测试,则无法将原始 SQL 发送到模拟数据库。
猜你喜欢
  • 2020-11-16
  • 2017-01-27
  • 2012-02-24
  • 2016-09-06
  • 1970-01-01
  • 1970-01-01
  • 2014-06-08
  • 1970-01-01
  • 2012-06-27
相关资源
最近更新 更多