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

如何禁用EF中枚举对应实体UnitOfMeasure的跟踪功能?

问题描述

我开发的新系统包含若干枚举类型,为保证引用完整性,没有仅以int类型存储枚举值,而是创建了对应的查找表(与枚举值一一对应,记录固定不变),使其能自动插入数据库。但在单元测试中批量插入对象时,出现如下错误:

System.InvalidOperationException: The instance of entity type 'UnitOfMeasure' cannot be tracked because another instance with the key value '{Id: Box}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.

相关代码

public class Line : EntityBase
{
    public int BillId { get; set; }

    public int Quantity { get; set; }

    public UnitOfMeasureEnum UnitOfMeasureId { get; set; }

    public virtual UnitOfMeasure UnitOfMeasure { get; set; } = null!;

    public IList<LineItem> LineItems { get; set; } = new List<LineItem>();
}

public class LineConfiguration : IEntityTypeConfiguration<Line>
{
    public void Configure(EntityTypeBuilder<Line> builder)
    {
        // Primary Key
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Id)
            .HasColumnOrder(0);

        // ... snip ...
            
        builder.Property(x => x.UnitOfMeasureId)
            .HasColumnOrder(8);

        builder.HasOne(x => x.UnitOfMeasure)
            .WithMany()
            .HasForeignKey(x => x.UnitOfMeasureId)
            .OnDelete(DeleteBehavior.Restrict);

        builder.HasMany(x => x.LineItems);
    }
}

public void Configure(EntityTypeBuilder<UnitOfMeasure> builder)
{
    builder.ToTable(
        TableName,
        x => x.HasComment("Table representation of UnitOfMeasureEnum for referential integrity. Do not edit without matching change to Enum  in code."));

    builder.Property(x => x.Id)
        .ValueGeneratedNever();

    builder.HasData(
        new List<UnitOfMeasure>
        {
            new() { Id = UnitOfMeasureEnum.Bin, Name = "Bin" },
            new() { Id = UnitOfMeasureEnum.Box, Name = "Box" },
            new() { Id = UnitOfMeasureEnum.Case, Name = "Case" },
            new() { Id = UnitOfMeasureEnum.Carton, Name = "Carton" },
            new() { Id = UnitOfMeasureEnum.Drum, Name = "Drum" },
            new() { Id = UnitOfMeasureEnum.Each, Name = "Each" },
            new() { Id = UnitOfMeasureEnum.Lot, Name = "Lot" },
            new() { Id = UnitOfMeasureEnum.Pail, Name = "Pail" },
            new() { Id = UnitOfMeasureEnum.Piece, Name = "Piece" },
            new() { Id = UnitOfMeasureEnum.Pallet, Name = "Pallet" },
            new() { Id = UnitOfMeasureEnum.Roll, Name = "Roll" },
            new() { Id = UnitOfMeasureEnum.Skid, Name = "Skid" },
        });
}

public class UnitOfMeasure
{
    public UnitOfMeasureEnum Id { get; set; }

    public string Name { get; set; }
}
解决方案

针对这种固定不变的枚举查找表,有几种实用的解决办法:

  • 禁用该实体的跟踪
    在UnitOfMeasure的配置类中添加无跟踪设置,让EF始终不跟踪该实体的实例:

    public void Configure(EntityTypeBuilder<UnitOfMeasure> builder)
    {
        // 原有配置代码...
        builder.HasNoTracking(); // 添加此配置
    }
    

    这是最彻底的解决方式,符合查找表固定不变的特性。

  • 批量插入时仅设置外键值
    因为UnitOfMeasure的记录是预插入且固定的,批量创建Line对象时,只需要给UnitOfMeasureId赋值枚举值即可,无需实例化UnitOfMeasure对象并赋值给导航属性:

    // 正确的批量创建示例
    var lines = new List<Line>
    {
        new Line { BillId = 1, Quantity = 5, UnitOfMeasureId = UnitOfMeasureEnum.Box },
        new Line { BillId = 2, Quantity = 3, UnitOfMeasureId = UnitOfMeasureEnum.Each }
        // 不需要设置UnitOfMeasure属性
    };
    dbContext.Lines.AddRange(lines);
    dbContext.SaveChanges();
    

    这种方式避免了EF尝试跟踪查找表实体,从根源上解决冲突。

  • 手动附加实体并设置状态(适合必须跟踪的场景)
    如果业务上需要保留跟踪,批量插入前可以先将需要用到的查找表实体附加到上下文,并设置为Unchanged状态,确保上下文只跟踪一个实例:

    // 预先附加需要用到的枚举实体
    var boxUom = new UnitOfMeasure { Id = UnitOfMeasureEnum.Box };
    dbContext.Attach(boxUom).State = EntityState.Unchanged;
    
    var eachUom = new UnitOfMeasure { Id = UnitOfMeasureEnum.Each };
    dbContext.Attach(eachUom).State = EntityState.Unchanged;
    
    // 再进行批量插入操作
    

    这种方法相对繁琐,仅推荐在必须跟踪查找表实体的场景下使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 00:47:11