C# – Entity Framework instance of entity type cannot be tracked

centity-framework-core

I have the following entities and context with 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);      
    });      

  }

}

Then I created a Language with translations for its names as follows:

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();

    }         

  }

}

I am creating a language with code "en" and translations for its name in English and Portuguese.

When I run the code I get the following error:

Unhandled Exception: System.InvalidOperationException:
The instance of entity type 'LanguageI18N' cannot be tracked because another instance of this type with the same key is already being tracked.
When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type).
If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities.
When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.

What am I missing?

UPDATE

The code works with InMemoryDatabase and the change suggested by Ivan Stoev:

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

But when I replaced InMemoryDatabase:

builder.UseInMemoryDatabase();

By SQL Server database:

  builder.UseSqlServer(yourConnectionString);

I get the following error:

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.

Any idea why?

Best Answer

Here

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);      

you are trying to map one and the same association represented by the Language -> LanguagesI18N navigation properties to two different FKs (LanguageCode and TranslationCode), which is not possible. The second line effectively overwrites the first, leading to incorrect setup.

Since you have two one-to-many relationships, and assuming the first line contains the correct setup for the first, the second can be setup by changing the last line to:

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

Such setup (w/o navigation property at both sides) was not possible in EF6, but perfectly supported in EF Core thanks to the parameterless overloads of the HasOne / HasMany methods. Just keep them (as well as WithOne / WithMany) in sync with your model navigation properties.