EF Core 7:无数据库生成键的实体更新/删除报错如何解决?
问题分析与解决方案
核心结论
EF Core完全支持手动设置非自增主键的场景,你遇到的DbUpdateConcurrencyException问题,根源是DateTime类型的精度不匹配,而非手动主键本身。
问题原因
数据库中Time列的精度(比如datetime2(7))与EF Core实体中DateTime类型的默认处理精度不一致。当你通过范围查询(Where(t => t.Time >= start && t.Time <= end))获取实体时,EF跟踪的Time值精度丢失,导致生成的UPDATE语句中,用于匹配主键的Time值与数据库实际存储的原始值存在细微差异,最终更新0行触发异常。
而你直接调用First()能成功,是因为该场景下EF读取的Time值精度刚好与数据库存储值完全匹配,没有差异。
解决方案
方案一:统一DateTime精度(推荐)
通过配置让EF Core实体的Time列精度与数据库保持一致,彻底解决问题。
方式1:使用Fluent API配置
在你的DbContext的OnModelCreating方法中添加配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<DataEntry>() .Property(d => d.Time) .HasPrecision(7); // 对应数据库datetime2(7)的精度,根据实际数据库列调整 }
方式2:使用数据注解配置
直接在实体的Time属性上添加注解:
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)] [Column(TypeName = "datetime2(7)")] // 与数据库列类型完全匹配 public DateTime Time { get; set; } = DateTime.Now;
方案二:临时规避方案(不推荐长期使用)
如果暂时不想修改精度配置,可以使用AsNoTracking查询,再手动附加实体并标记修改状态:
var result = ctx.DataEntries .AsNoTracking() .Where(t => t.Time >= start && t.Time <= end) .OrderBy(t => t.Time).First(); ctx.DataEntries.Attach(result); ctx.Entry(result).Property(d => d.StatusCode).IsModified = true; ctx.SaveChanges();
关于新增Id列的迁移问题
如果最终选择新增自增Id列作为主键,EF Core的迁移完全支持现有数据的处理,无需手动填充随机数:
- 修改实体类:
[Table("DataEntries")] public class DataEntry { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [Index(nameof(Time))] public DateTime Time { get; set; } = DateTime.Now; public byte StatusCode { get; set; } }
- 生成并执行迁移:
Add-Migration AddPrimaryKeyId Update-Database
执行迁移时,数据库会自动为现有数千条数据生成连续的自增Id值,无需手动干预。你之前尝试复合主键仍有问题,大概率是手动填充随机Id时出现了重复值,或者精度问题仍未解决。
内容的提问来源于stack exchange,提问作者Cleo
相关产品推荐
相关产品推荐

