LINQ 中的更新查询包含 WHERE 子句中的所有列,而不仅仅是主键列

Posted

技术标签:

【中文标题】LINQ 中的更新查询包含 WHERE 子句中的所有列,而不仅仅是主键列【英文标题】:Update query in LINQ contains all columns in WHERE clause instead of just the primary key column 【发布时间】:2021-03-20 15:31:59 【问题描述】:

我正在使用 Linq 更新表格中的单个列,请取下面的虚构表格。

MyTable(PKID、ColumnToUpdate、SomeRandomColumn)

var row = (from x in DataContext.MyTable
           where b.PKID == 5
           select x).FirstOrDefault();

row.ColumnToUpdate = 20;
DataContext.SubmitChanges();

这会按预期将列更新,这并不奇怪。但是,当我检查生成的 SQL 命令时,它会这样做:

UPDATE [dbo].[MyTable ]
SET [ColumnToUpdate ] = @p2
WHERE ([PKID] = @p0) AND ([SomeRandomColumn] = @p1) 

这是在执行更新,但前提是所有列都与 Entity 期望的值匹配,而不是单独引用主键列。

如果一个数据库列被另一个进程改变了,这在这个特定的项目中是非常可行的;例如。在获取您想要操作的行、计算您想要将值设置为的更改以及作为一批行发出更新命令之间存在一个窗口。在这种情况下,查询将导致异常,导致部分更新,除非我捕获、重新加载数据并重新发送单个查询。它还有一个缺点,即行信息可能非常大(例如,包含 html 标记),并且整个信息都会传递给 SQL,并且在处理较大的批次时会降低系统速度。

有没有办法让 Linq / Entity 仅根据 Where 子句中的 PK 列发出更新命令?

【问题讨论】:

检查 DBML。您可能有一个 TimeStamp 列或一个带有更新检查的列。 是的,这已经成功了。默认情况下,所有列的更新检查标志都设置为“始终”。更改整个数据库有点任务,但至少我可以针对少数问题表进行更改。谢谢,如果您想发布作为答案,我会接受。 【参考方案1】:

我从未在生产项目中使用过 LINQ-to-SQL,而且我从未意识到它会默认应用乐观并发1

这是默认行为:

    如果表没有Timestamp/Rowversion2所有 列在 DBML 中将“更新检查”设置为“始终”(主键除外列和计算列,即所有可更新的列)。 如果表确实有 Timestamp/Rowversion 列,则此列在 DBML 中将“时间戳”设置为“真”,并且所有列都具有“更新检查”=“从不”。

“更新检查”或“时间戳”将列标记为并发标记。这就是为什么在更新语句中,您会在(并非如此)“随机”列上看到这些附加谓词。显然,模型中的表没有 Timestamp/Rowversion 列,因此更新会检查表中所有可更新列的值。


1 乐观并发:更新记录时不设置排他锁,但更新时检查所有或选定列的现有值。如果其他用户在获取数据和保存数据之间更改了其中一个列值,则会发生更新异常。

2 数据类型为TimestampRowversion 的列会在记录更新时自动递增,因此会检测到此记录的所有并发更改。

【讨论】:

以上是关于LINQ 中的更新查询包含 WHERE 子句中的所有列,而不仅仅是主键列的主要内容,如果未能解决你的问题,请参考以下文章

Linq 中的 Groupby 和 where 子句

Linq to Entities 中的动态 where 子句 (OR)

包含 UTC 时区转换的 Linq 查询 where 子句

LINQ to SQL查询中的C#Dynamic WHERE子句

linq to sql select和where的区别

C#图解教程 第十九章 LINQ