EF 更新大量的数据时出现重复键错误
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EF 更新大量的数据时出现重复键错误相关的知识,希望对你有一定的参考价值。
咨询区
ChsharpNewbie:
当我把大量的数据插入到数据库时 (PostgreSQL 12
和 Entity Framework Core
),我得到了如下的报错。
fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
Failed executing DbCommand (197ms) [Parameters=[@p0='?', @p1='?', @p2='?' (DbType = DateTimeOffset), @p3='?'], CommandType='Text', CommandTimeout='30']
INSERT INTO "FileInfos" ("FileId", "FileName", "LastModifiedDateTime", "Path")
VALUES (@p0, @p1, @p2, @p3);
fail: Microsoft.EntityFrameworkCore.Update[10000]
An exception occurred in the database while saving changes for context type 'PostgreSQLConnect.ContextModels.WebhookContext'.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> Npgsql.PostgresException (0x80004005): 23505: duplicate key value violates unique constraint "PK_FileInfos
Severity: FEHLER
SqlState: 23505
MessageText: double key value violates unique constraint »PK_FileInfos«
Detail: Detail redacted as it may contain sensitive data. Specify 'Include Error Detail' in the connection string to include this information.
SchemaName: public
TableName: FileInfos
ConstraintName: PK_FileInfos
File: d:\\pginstaller_12.auto\\postgres.windows-x64\\src\\backend\\access\\nbtree\\nbtinsert.c
Line: 570
Routine: _bt_check_unique
这其中一些数据需要被更新,一些数据需要被创建,在一定数据量下这个方法比较稳定,但如果超过阈值后就会抛出如上的错误,我的代码如下:
private async Task SaveFileInfos(FileInfo fileInfo)
{
var foundFileInfo = _context.FileInfos.Where(f => f.FileId == fileInfo.FileId).FirstOrDefault();
if (foundFileInfo == null)
{
await _context.FileInfos.AddAsync(fileInfo);
}
else
{
foundFileInfo.FileName = fileInfo.FileName;
foundFileInfo.LastModifiedDateTime = fileInfo.LastModifiedDateTime;
foundFileInfo.Path = fileInfo.Path;
}
await _context.SaveChangesAsync();
}
我的类定义如下:
public class FileInfo : IFileInfo
{
[Key]
public string FileId {get; set;}
public string FileName {get; set;}
public DateTimeOffset? LastModifiedDateTime {get; set;}
public string Path {get; set;}
}
Context类如下:
public class WebhookContext : DbContext
{
public WebhookContext(DbContextOptions<WebhookContext> options) : base(options) { }
public DbSet<FileInfo> FileInfos { get; set; }
}
然后在 loop 中做数据库保存。
private async Task ConvertAndSaveFiles(IDriveItemDeltaCollectionPage files)
{
foreach (var file in files)
{
await SaveFileInfos(file.Name, file.Id, file.LastModifiedDateTime, file.ParentReference.Path);
}
}
请问我这是哪里写的有问题?
回答区
Edd:
我觉得你要做两点修改。
将 FirstOrDefault 改成 FirstOrDefaultAsync。
where 查询也是多余的。
改造后如下:
private async Task SaveFileInfos(FileInfo fileInfo)
{
//update your code to use FirstOrDefaultAsync
var foundFileInfo = await _context.FileInfos.FirstOrDefaultAsync(f => f.FileId == fileInfo.FileId);
if (foundFileInfo == null)
{
await _context.FileInfos.AddAsync(fileInfo);
}
else
{
foundFileInfo.FileName = fileInfo.FileName;
foundFileInfo.LastModifiedDateTime = fileInfo.LastModifiedDateTime;
foundFileInfo.Path = fileInfo.Path;
}
// move this outside the for loop.
// this will round trip to Db in EVERY fileInfo, not an optimal solution.
await _context.SaveChangesAsync();
}
考虑到 SaveChangesAsync 是序列化到数据库,可以移到循环体外。
private async Task ConvertAndSaveFiles(IDriveItemDeltaCollectionPage files)
{
foreach (var file in files)
{
await SaveFileInfos(file.Name, file.Id, file.LastModifiedDateTime, file.ParentReference.Path);
}
// this will save everything to Db in just 1 round trip
await _context.SaveChangesAsync();
}
点评区
我个人感觉,这里报错的原因是: 本应该全异步的写法里面又掺杂了同步的写法,这是一种很鸡肋的做法,数据量稍微大一些之后就会有各种问题,这也是一个好的经验教训。
以上是关于EF 更新大量的数据时出现重复键错误的主要内容,如果未能解决你的问题,请参考以下文章
尝试在 Web API Core 中使用 EF Core 在启动时更新数据库时出现“'无法访问已处置的对象”错误