You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的迁移完全支持现有数据的处理,无需手动填充随机数:

  1. 修改实体类:
[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; }
}
  1. 生成并执行迁移:
Add-Migration AddPrimaryKeyId
Update-Database

执行迁移时,数据库会自动为现有数千条数据生成连续的自增Id值,无需手动干预。你之前尝试复合主键仍有问题,大概率是手动填充随机Id时出现了重复值,或者精度问题仍未解决。

内容的提问来源于stack exchange,提问作者Cleo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 10:46:21