Home > other >  "The instance of entity type cannot be tracked" error when adding detached entity tree
"The instance of entity type cannot be tracked" error when adding detached entity tree

Time:01-27

I am unable to add the Deal entity because Buyer and Seller are referencing the same city "City1". I work with detached entities here because I receive the whole model through API.

// This is the result of Deal entity deserialization
var deal = new Deal
{
    Id = "D1",
    Buyer = new Person
    {
        Id = "P1",
        Name = "Person1",
        City = new City { Name = "City1" }
    },
    Seller = new Person
    {
        Id = "P2",
        Name = "Person2",
        City = new City { Name = "City1" }
    }
};

_dbContext.Deals.Add(deal);
_dbContext.SaveChanges();

The error is

System.InvalidOperationException: 'The instance of entity type 'City' cannot be tracked because another instance with the same key value for {'Name'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'

Here is how entities are set up:

modelBuilder.Entity<City>(x =>
{
    x.HasKey(e => e.Name);
});

modelBuilder.Entity<Person>(x =>
{
    x.HasKey(e => e.Id);
    x.Property(e => e.Name);
    x.HasOne(e => e.City).WithMany();
});

modelBuilder.Entity<Deal>(x =>
{
    x.HasKey(e => e.Id);
    x.HasOne(e => e.Buyer).WithMany();
    x.HasOne(e => e.Seller).WithMany();
});

What is the proper way of working with such kind of entities? I use EF Core 6

CodePudding user response:

Your two Person objects would need to reference the same object for City. Alternatively, expose and use the FK property (which appears to be the city's Name property) instead of the navigation property for this operation.

If you want to keep using the City objects in JSON, then you'll need to preserve references. For example, here's the documentation for System.Text.Json

using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace PreserveReferences
{
    public class Employee
    {
        public string Name { get; set; }
        public Employee Manager { get; set; }
        public List<Employee> DirectReports { get; set; }
    }

    public class Program
    {
        public static void Main()
        {
            Employee tyler = new()
            {
                Name = "Tyler Stein"
            };

            Employee adrian = new()
            {
                Name = "Adrian King"
            };

            tyler.DirectReports = new List<Employee> { adrian };
            adrian.Manager = tyler;

            JsonSerializerOptions options = new()
            {
                ReferenceHandler = ReferenceHandler.Preserve,
                WriteIndented = true
            };

            string tylerJson = JsonSerializer.Serialize(tyler, options);
            Console.WriteLine($"Tyler serialized:\n{tylerJson}");

            Employee tylerDeserialized =
                JsonSerializer.Deserialize<Employee>(tylerJson, options);

            Console.WriteLine(
                "Tyler is manager of Tyler's first direct report: ");
            Console.WriteLine(
                tylerDeserialized.DirectReports[0].Manager == tylerDeserialized);
        }
    }
}

// Produces output like the following example:
//
//Tyler serialized:
//{
//  "$id": "1",
//  "Name": "Tyler Stein",
//  "Manager": null,
//  "DirectReports": {
//    "$id": "2",
//    "$values": [
//      {
//        "$id": "3",
//        "Name": "Adrian King",
//        "Manager": {
//          "$ref": "1"
//        },
//        "DirectReports": null
//      }
//    ]
//  }
//}
//Tyler is manager of Tyler's first direct report:
//True
  •  Tags:  
  • Related