-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Closed
Milestone
Description
If I update an owned entity a call to SaveChanges crashes with the exception below.
System.InvalidOperationException: The instance of entity type 'Person.Address#Address' cannot be tracked because another instance with the same key value for {'PersonId'} 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.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, Boolean acceptChanges, Boolean forceStateWhenUnknownKey)
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.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState entityState, Boolean forceStateWhenUnknownKey)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NavigationFixer.NavigationReferenceChanged(InternalEntityEntry entry, INavigation navigation, Object oldValue, Object newValue)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntryNotifier.NavigationReferenceChanged(InternalEntityEntry entry, INavigation navigation, Object oldValue, Object newValue)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectNavigationChange(InternalEntityEntry entry, INavigation navigation)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ChangeDetector.DetectChanges(IStateManager stateManager)
at Microsoft.EntityFrameworkCore.ChangeTracking.ChangeTracker.DetectChanges()
at Microsoft.EntityFrameworkCore.DbContext.TryDetectChanges()
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
class Program
{
static void Main(string[] args)
{
try
{
int id;
using (var context = new TestDbContext())
{
context.Database.Migrate();
var result = context.Persons.Add(new Person("John Doe", new Address("SomeStreet")));
context.SaveChanges();
id = result.Entity.Id;
}
using (var context = new TestDbContext())
{
var person = context.Find<Person>(id);
person.Move(new Address("OtherStreet"));
context.SaveChanges();
}
}
catch (Exception e)
{
Console.WriteLine(e);
Console.ReadLine();
}
}
}
public class TestDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=.;Database=OwnOneTest;Integrated Security=SSPI");
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().OwnsOne(x => x.Address);
base.OnModelCreating(modelBuilder);
}
public DbSet<Person> Persons { get; set; }
}
public class Person
{
[Obsolete("ORM constructor", true)]
protected Person() {}
public Person(string name, Address address)
{
Name = name;
Address = address;
}
public int Id { get; private set; }
public string Name { get; private set; }
public Address Address { get; private set; }
public void Move(Address address)
{
Address = address ?? throw new ArgumentNullException(nameof(address));
}
}
public class Address
{
[Obsolete("ORM constructor", true)]
protected Address() { }
public Address(string street)
{
Street = street;
}
public string Street { get; private set; }
}
If this is not the correct way to map DDD Entities and Value Objects, then how should I do it?
andriysavin, d1mnewz, Deilan and HermannGruber