Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Entity Framework instance of entity type cannot be tracked #7599

Closed
mdmoura opened this issue Feb 12, 2017 · 11 comments
Closed

Entity Framework instance of entity type cannot be tracked #7599

mdmoura opened this issue Feb 12, 2017 · 11 comments
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@mdmoura
Copy link

mdmoura commented Feb 12, 2017

Problem

I am not able to create two LanguageI18N instances associated with one Language instance. The same error occurs using SQLServer or an InMemoryDatabase.

Is this a bug? Am I missing something? Any way to go around this problem?

Exception

Exception message:
Stack trace:
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 explici
tly 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 o
ne entity instance with a given key value is attached to the context.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.KeyPropertyChanged(InternalEntityEntry entry, IProperty property, IReadOnlyList`1 containingPrincipalKeys, IReadOnlyList`
1 containingForeignKeys, Object oldValue, Object newValue)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.KeyPropertyChanged(InternalEntityEntry entry, IProperty property, IReadOnlyList`1 keys, IReadOnlyList`1 forei
gnKeys, Object oldValue, Object newValue)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectKeyChange(InternalEntityEntry entry, IProperty property)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.PropertyChanged(InternalEntityEntry entry, IPropertyBase property, Boolean setModified)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetProperty(IPropertyBase propertyBase, Object value, Boolean setModified)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.SetForeignKeyProperties(InternalEntityEntry dependentEntry, InternalEntityEntry principalEntry, IForeignKey foreignKey, B
oolean setModified)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.FixupToDependent(InternalEntityEntry principalEntry, InternalEntityEntry dependentEntry, IForeignKey foreignKey, Boolean
setModified)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.DelayedFixup(InternalEntityEntry entry, INavigation navigation, InternalEntityEntry referencedEntry)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.InitialFixup(InternalEntityEntry entry, ISet`1 handledForeignKeys, Boolean fromQuery)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.StateChanged(InternalEntityEntry entry, EntityState oldState, Boolean fromQuery)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.StateChanged(InternalEntityEntry entry, EntityState oldState, Boolean fromQuery)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode node)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph(EntityEntryGraphNode node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph(EntityEntryGraphNode node, Func`2 handleNode)
   at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState)

Steps to reproduce

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

        }         

      }

    }

Further technical details

EF Core version: 1.1.0
Database Provider: Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore.InMemory
Operating system: Windows 10
IDE: Visual Studio Code

@mdmoura
Copy link
Author

mdmoura commented Feb 13, 2017

An update on my code. By changing

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

to

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

It solves the problem for InMemoryDatabase but when using SQLServer I get the 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.

BTW, not sure if the change in the configuration is the way to go ... It was just something I tried.

@ajcvickers
Copy link
Member

@mdmoura With these two lines:

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

the foreign key for the relationship is first set to be LanguageCode and then changed to be TranslationCode. I'm not entirely sure what your intention is here, but it seems like the FK for this relationship should probably just be LanguageCode, meaning the second line should just be removed.

@mdmoura
Copy link
Author

mdmoura commented Feb 13, 2017

@ajcvickers As an example I am posting a few records of Languages and LanguagesI18N tables:

Languages
LanguageCode
en
pt

LanguagesI18N
Language Code, Translation Code, Name
en en English
en pt Inglês
pt en Portuguese
pt pt Português

So Languages holds all the unique Language codes ... LanguagesI18N holds the names translations.
en en English > en code name in English is English
en pt Inglês > en code name in Portuguese is Inglês
and so on ...

So both LanguageCode and TranslationCode are FK in table LanguagesI18N related to LanguageCode in table Languages.

I am using similar structure in other tables in this MultiLanguage database. Example:

Product
Id Price
1 100
2 50

ProductI18N
Id TranslationCode Name
1 en Toy
1 pt Brinquedo
2 en Pencil
2 pt Lápis

The point is that in ProductsI18N the Id and TranslationCode are different and FK to different tables.

In LanguagesI18N table LanguageCode and TranslationCode are FK's to same table but filling different purposes. LanguageCode is the language it is translating, TranslationCode is the language of translation.

Does it make sense? For me it seems a pretty normal scenario ... I am just saving all Languages and their names in different languages.

@ajcvickers
Copy link
Member

@mdmoura That makes sense now. You will need to specify these as two different relationships, that use different (or no) navigation properties between the entities. This is what you did in your first comment--you made a new relationship for the TranslationCode FK which had no navigation properties. However, it seems that while trying to enforce this constraint SQL Server was unable to find a row in the Languages table with a primary key value of "pt". Are you sure that the Languages table contains this row?

@mdmoura
Copy link
Author

mdmoura commented Feb 13, 2017

@ajcvickers, The FK's have two different names, LanguageCode and TranslationCode but after running the migration in fact I get two columns LanguageCode and LanguageCode1. I think this shouldn't happen ...

@ajcvickers
Copy link
Member

@mdmoura Agreed this shouldn't happen. We will discuss this in the team. As a workaround, try using .HasPrincipalKey(e => e.LanguageCode) to force both relationships to use the same primary key.

@mdmoura
Copy link
Author

mdmoura commented Feb 13, 2017

@ajcvickers I tried the following as you suggested:

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

The database is correctly created but when I try to create a few records I got the 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 n o 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.

I then tried to keep only WithMany() on the second relation:

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

But I got the following error when creating the records:

Unhandled Exception: Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Invalid column name 'Lan guageCode1'. Invalid column name 'LanguageCode1'.

Any way to go around this?

@AndriySvyryd
Copy link
Member

@mdmoura You cannot reuse the same navigation for two relationships, try this:

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

@mdmoura
Copy link
Author

mdmoura commented Feb 14, 2017

@AndriySvyryd Thank you, that work. But I ended adding to navigation properties in Language to the two FK in LanguageI18N because it is better for navigation and in fact remove all the problems I add so far.

@AndriySvyryd
Copy link
Member

This is probably already fixed in 2.0.0

@ajcvickers ajcvickers modified the milestones: 2.0.0-preview1, 2.0.0 Apr 19, 2017
@AndriySvyryd
Copy link
Member

Confirmed the other key is no longer created in 2.0.0

@AndriySvyryd AndriySvyryd removed their assignment Jun 2, 2017
@AndriySvyryd AndriySvyryd added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Jun 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Projects
None yet
Development

No branches or pull requests

4 participants