【问题标题】:Entity Framework instance of entity type cannot be tracked无法跟踪实体类型的实体框架实例
【发布时间】:2017-06-30 12:25:18
【问题描述】:

我在 Entity Framework Core 1.1 中有以下实体和上下文:

public class Language {
  public String LanguageCode { get; set; }
  public virtual ICollection<LanguageI18N> LanguagesI18N { get; set; } = new List<LanguageI18N>();
}

public class LanguageI18N {
  public String LanguageCode { get; set; }
  public String TranslationCode { get; set; }
  public String Name { get; set; }        
  public virtual Language Language { get; set; }
}

public class Context : DbContext {

  public DbSet<Language> Languages { get; set; }
  public DbSet<LanguageI18N> LanguagesI18N { get; set; }

  public Context(DbContextOptions options) : base(options) { }

  protected override void OnModelCreating(ModelBuilder builder) {

    base.OnModelCreating(builder);

    builder.Entity<Language>(b => {
      b.ToTable("Languages");
      b.HasKey(x => x.LanguageCode);
      b.Property(x => x.LanguageCode).IsRequired(true).HasMaxLength(2).ValueGeneratedNever();        
    });

    builder.Entity<LanguageI18N>(b => {
      b.ToTable("LanguagesI18N");
      b.HasKey(x => new { x.LanguageCode, x.TranslationCode });      
      b.Property(x => x.LanguageCode).HasMaxLength(2).IsRequired(true);
      b.Property(x => x.TranslationCode).HasMaxLength(2).IsRequired(true);
      b.HasOne(x => x.Language).WithMany(x => x.LanguagesI18N).HasForeignKey(x => x.LanguageCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);
      b.HasOne(x => x.Language).WithMany(x => x.LanguagesI18N).HasForeignKey(x => x.TranslationCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);      
    });      

  }

}

然后我创建了一种语言,其名称的翻译如下:

public class Program {

  public static void Main(String[] args) {

    DbContextOptionsBuilder builder = new DbContextOptionsBuilder<Context>();

    builder.UseInMemoryDatabase();

    using (Context context = new Context(builder.Options)) {

      Language language = new Language { LanguageCode = "en" };

      language.LanguagesI18N.Add(new LanguageI18N { LanguageCode = "en", TranslationCode = "en", Name = "English" });
      language.LanguagesI18N.Add(new LanguageI18N { LanguageCode = "en", TranslationCode = "pt", Name = "Inglês" });

      context.Languages.Add(language);

      context.SaveChanges();

    }         

  }

}

我正在创建一种语言,代码为“en”,并用英语和葡萄牙语翻译其名称。

当我运行代码时,我收到以下错误:

未处理的异常:System.InvalidOperationException: 无法跟踪实体类型“LanguageI18N”的实例,因为已在跟踪具有相同键的此类型的另一个实例。 添加新实体时,对于大多数键类型,如果未设置键(即,如果键属性为其类型分配了默认值),则会创建一个唯一的临时键值。 如果您为新实体显式设置键值,请确保它们不会与现有实体或为其他新实体生成的临时值发生冲突。
附加现有实体时,请确保只有一个具有给定键值的实体实例附加到上下文。

我错过了什么?

更新

该代码适用于 InMemoryDatabase 以及 Ivan Stoev 建议的更改:

b.HasOne<Language>().WithMany().HasForeignKey(x => x.TranslationCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);

但是当我替换 InMemoryDatabase 时:

builder.UseInMemoryDatabase();

通过 SQL Server 数据库:

  builder.UseSqlServer(yourConnectionString);

我收到以下错误:

Unhandled Exception: Microsoft.EntityFrameworkCore.DbUpdateException: 
An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: 
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_LanguagesI18N_Languages_TranslationCode". 
The conflict occurred in database "TestDb", table "dbo.Languages", column 'LanguageCode'.
The statement has been terminated.

知道为什么吗?

【问题讨论】:

  • 你是如何创建你的上下文对象的?是被注射了吗?您是如何将其添加到您的 DI 容器中的?
  • 我只是在控制台应用程序中使用连接字符串创建上下文,它运行良好,因为我能够获取 Languages 表的计数。
  • 所以你在某处做var context = new Context();? (PS 命名你的上下文类 Context 令人困惑!)
  • 是的,我是......我使用 Context 作为类型,因为目前我只是在测试这个。
  • 我无法理解您的代码,因为您显然是在删减要在此处发布的内容,但这样做您提供的代码无法编译。例如language.LanguagesI18N 会给你一个空引用异常。还有其他问题。你能提供一个minimal reproducible example

标签: c# entity-framework-core


【解决方案1】:

这里

b.HasOne(x => x.Language).WithMany(x => x.LanguagesI18N).HasForeignKey(x => x.LanguageCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);
b.HasOne(x => x.Language).WithMany(x => x.LanguagesI18N).HasForeignKey(x => x.TranslationCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);      

您正试图将Language -> LanguagesI18N 导航属性表示的同一个关联映射到两个不同的 FK(LanguageCode 和 TranslationCode),这是不可能的。第二行有效地覆盖了第一行,导致设置不正确。

由于您有两个one-to-many 关系,并且假设第一行包含第一行的正确设置,因此可以通过将最后一行更改为:

b.HasOne<Language>().WithMany().HasForeignKey(x => x.TranslationCode).IsRequired(true).OnDelete(DeleteBehavior.Restrict);

这种设置(两边都没有导航属性)在 EF6 中是不可能的,但由于 HasOne / HasMany 方法的无参数重载,EF Core 完全支持这种设置。只需让它们(以及WithOne / WithMany)与您的模型导航属性保持同步即可。

【讨论】:

  • 您的建议仅适用于 InMemoryDatabase 而不是真正的 SQL 数据库......至少看起来是这样。我在问题中添加了一个更新,解释了我的尝试。
  • 实际上,新异常表明上述工作正常 - 已创建正确的 FK 约束。现在被您的插入违反了,这意味着您指定了TranslationCode,它在Language 表中不存在,对吗?
  • @IvanStoev 就在这里。语言代码 pt 未添加到语言表中,因此您无法添加专门针对所需关系引用它的子项。
猜你喜欢
  • 2019-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-04
  • 1970-01-01
  • 1970-01-01
  • 2022-01-26
  • 1970-01-01
相关资源
最近更新 更多