sql - 为什么会抛出 DbUpdateConcurrencyException?
问题描述
我正在使用 SQL 服务器,并且我有一个表,其目的是保存树状结构:
create table TableName (
Id bigint identity,
Name nvarchar(50) null,
RootId bigint null,
ParentId bigint null,
Path nvarchar(100) null,
constraint PK_TableName primary key (Id)
)
“路径”列值由 INSTEAD OF INSERT、UPDATE 触发器生成。我正在使用 EFCore 3.1,每次尝试将记录添加到表中时,都会抛出 DbUpdateConcurrencyException。
我错过了什么 - 我该如何解决这个问题?
顺便说一句,当我禁用触发器插入通道时,当我发送常规 INSERT 命令时触发器工作。
感谢 Panagiotis 为您解答。我理解逻辑,但它仍然不起作用。我试过这个:
protected virtual void MapTableName(EntityTypeBuilder<TableName> config)
{
config.ToTable("TableName");
config.HasKey(t => t.Id);
config.Property(t => t.Id).ValueGeneratedOnAdd().IsConcurrencyToken();
config.Property(t => t.Name).IsConcurrencyToken().HasMaxLength(50);
config.Property(t => t.Description).IsConcurrencyToken().HasMaxLength(100);
config.Property(t => t.RootId).IsConcurrencyToken();
config.Property(t => t.ParentId).IsConcurrencyToken();
config.Property(t => t.Path).HasMaxLength(100).ValueGeneratedOnAddOrUpdate();
config.Property(t => t.TypeId).IsConcurrencyToken();
config.Property(t => t.IsActive).IsConcurrencyToken();
config.HasOne(t => t.LocationType).WithMany(t => t.TableNames).HasForeignKey(t => t.TypeId);
config.HasOne(t => t.ParentTableName).WithMany(t => t.ChilTableNames).HasForeignKey(t => t.ParentId);
config.HasOne(t => t.RootTableName).WithMany(t => t.ChildTableNamesAll).HasForeignKey(t => t.RootId);
}
但我得到相同的答案:
"Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions."
解决方案
问题是Path
.
EF Core 默认使用乐观并发,假设冲突(即另一个连接对同一记录的更改)很少见。为确保自加载记录后值未更改,EF Core 将检查rowversion
列的值(如果存在),或将所有原始属性值与表的值进行比较。如果Path
在 EF 不知道的情况下更改,则会出现并发冲突。
解决此问题的最佳方法是向表中添加一rowversion
列,并将其添加到具有Timestamp
属性(rowversion 的旧名称)的模型中。在 SQL Server 中,每次更新时服务器都会自动更新 rowversion。这样,并发检查只使用一个小的二进制值:
class MyClass
{
public int Id {get;set;}
public string Name{get;set;}
public string Path {get;set;}
[Timestamp]
public byte[] Timestamp { get; set; }
}
另一种选择是仅使用ConcurrencyCheck属性标记几个属性。在这种情况下,除了: Path
{
[ConcurrencyCheck]
public int Id {get;set;}
[ConcurrencyCheck]
public string Name{get;set;}
public string Path {get;set;}
}
推荐阅读
- android - Android 小部件 - 无法访问共享首选项
- python - AWS Lambda python 从 DynamoDB 事件的响应中以 HTML 格式发送电子邮件正文
- html - 所需的 HTML 在我的网页上不起作用
- javascript - 在 JavaScript RegExp 中使用数组的正确方法是什么?
- javascript - 如何为 Object.assign 中的键分配多个值
- java - 以编程方式添加 Spring 异常处理程序,没有 @ExceptionHandler 注释(或任何其他注释)?
- python - 为什么你必须重塑 Keras/Tensorflow 2 中的输入?
- python - 使用 nvprof 分析 Tensorflow 代码时如何捕获 GPU 数据?
- java - Spring Boot 序列化 JSON
- php - 对多维数组中的行进行分组并对每组中的“计数”元素求和