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

The instance of entity type X cannot be tracked because another instance of this type with the same key is already being tracked #6490

Closed
jarzimichal opened this issue Sep 8, 2016 · 3 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

@jarzimichal
Copy link

jarzimichal commented Sep 8, 2016

Hi,

I'm getting this error when I try to Insert domain object containing 2 level deep sub objects:

The instance of entity type 'Toy' 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.

I have shared my project here: https://github.com/jarzimichal/ef7-tracking-issue/tree/master/src/EF7

Here is my code:

public abstract class Entity
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int IdRow { get; set; }
    }

    public class Parent : Entity
    {
        public string Name { get; set; }

        public ICollection<Child> Childs { get; set; }
    }

    public class Child : Entity
    {
        public string Name { get; set; }
        public ICollection<Toy> Toys { get; set; }
    }

    public class Toy : Entity
    {
        public string Name { get; set; }
    }
public static void Main(string[] args)
        {
            var opt = new DbContextOptionsBuilder<RootContext>();
            opt.UseSqlServer("Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=ef7-test-db; Integrated Security=True;");
            //opt.UseInMemoryDatabase();

            using (IDbContext db = new RootContext(opt.Options))
            {
                var rep = new Repository(db);

                var parent = new Parent
                {
                    Name = "Parent",
                    Childs = new List<Child>
                    {
                        new Child {Name = "Child1", Toys = new List<Toy>
                        {
                            new Toy { Name = "Toy1"}, //When this line is commented, code works fine
                            new Toy { Name = "Toy2"},
                        }},
                        new Child {Name = "Child1"}
                    }
                };

                rep.Insert(parent);
                rep.SaveChanges();
            }
        }

I have investigated the entity in debugger and looks like EF assigned the same '-2147482647' value to IdRow property in both Toys objects.

@ajcvickers
Copy link
Contributor

Notes for triage: This is happening because Toy.IdRow is being used as the FK for the one-to-many relationship with Child, but IdRow is also the PK. This is being done by convention--see full simplified repro below. This means that the property is getting its value via FK propagation, but this obviously doesn't work for a PK in a one-to-many relationship.

Note that if the line relationship.DeleteBehavior = DeleteBehavior.Restrict; is removed, then Migrations throws "Cascading foreign key 'FK_Toy_Child_IdRow' cannot be created where the referencing column 'Toy.IdRow' is an identity column."

Full simplified repro:

public class RootContext : DbContext
{
    public RootContext(DbContextOptions<RootContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Parent>();
        modelBuilder.Entity<Child>();
        modelBuilder.Entity<Toy>();

        //Remove cascading deletes
        foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
        {
            relationship.DeleteBehavior = DeleteBehavior.Restrict;
        }

        base.OnModelCreating(modelBuilder);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var opt = new DbContextOptionsBuilder<RootContext>();
        opt.UseSqlServer(
            "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=ef7-test-db; Integrated Security=True;");

        using (DbContext db = new RootContext(opt.Options))
        {
            db.Database.EnsureDeleted();
            db.Database.EnsureCreated();

            var parent = new Parent
            {
                Name = "Parent",
                Childs = new List<Child>
                {
                    new Child
                    {
                        Name = "Child1",
                        Toys = new List<Toy>
                        {
                            new Toy {Name = "Toy1"},
                            new Toy {Name = "Toy2"},
                        }
                    },
                    new Child {Name = "Child1"}
                }
            };

            db.Add(parent);
            db.SaveChanges();
        }
    }
}

public class Parent
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int IdRow { get; set; }
    public string Name { get; set; }

    public ICollection<Child> Childs { get; set; }
}

public class Child
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int IdRow { get; set; }
    public string Name { get; set; }
    public ICollection<Toy> Toys { get; set; }
}

public class Toy
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int IdRow { get; set; }
    public string Name { get; set; }
}

@ajcvickers
Copy link
Contributor

@jarzimichal The workaround for this is to specify an FK property for the Toy class. There are various ways of doing this, but the easiest is just to add a property that will be recognized by convention as the FK. For example:

public class Toy
{
    public int ChildId { get; set; } // Will be used as the FK
    public string Name { get; set; }
}

@jarzimichal
Copy link
Author

Awesome! Thanks a lot.
That was indeed the problem and now everything works like a charm :)

Cheers,
Michal!

@rowanmiller rowanmiller reopened this Sep 12, 2016
@rowanmiller rowanmiller added this to the 1.1.0 milestone Sep 12, 2016
@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 Oct 12, 2016
@AndriySvyryd AndriySvyryd removed their assignment Oct 12, 2016
@ajcvickers ajcvickers modified the milestones: 1.1.0-preview1, 1.1.0 Oct 15, 2022
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