Entity History does not record "Delete" Owned Entity changes
Environment
Abp.ZeroCore.EntityFrameworkCore11.0.0- .NET 10 / EF Core 10
- SQL Server
Summary
When we remove an [Owned] entity, it does not create entity history.
Steps to reproduce
[Audited]
public class Person
{
public int Id { get; set; }
public string Name { get; set; };
public Address? HomeAddress { get; set; }
}
[Owned]
public class Address
{
public string Street { get; set; };
}
public class MyDbContext : AbpDbContext
{
public DbSet<Person> People => Set<Person>();
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }
}// 1. Create a Person with a HomeAddress.
var person = new Person { Name = "John", HomeAddress = new Address { Street = "Main St" } };
context.People.Add(person);
await context.SaveChangesAsync();
// 2. Remove the HomeAddress.
var loaded = await context.People.FirstAsync();
loaded.HomeAddress = null;
await context.SaveChangesAsync();Actual behavior
For step 1, it produces a property change (as expected):
| EntityTypeFullName | EntityId | PropertyName | OriginalValue | NewValue |
|---|---|---|---|---|
| Person.Address | Id of the Person | Street | NULL | "Main Street" |
For step 2, it didn't produce any entity history.
Expected behavior
Step 2 should produce the following (as the opposite of "create" history):
| EntityTypeFullName | EntityId | PropertyName | OriginalValue | NewValue |
|---|---|---|---|---|
| Person.Address | Id of the Person | Street | "Main Street" | NULL |
Root cause
AbpZeroCommonDbContext.SaveChangesAsync()callsEntityHistoryHelper.CreateEntityChangeSet(ChangeTracker.Entries().ToList()): https://github.com/aspnetboilerplate/aspnetboilerplate/blob/06fc849206855c9e2b8cffaa3dc9200edd9fffe2/src/Abp.ZeroCore.EntityFrameworkCore/Zero/EntityFrameworkCore/AbpZeroCommonDbContext.cs#L203-L215At this point, the owned entity's state is
Deleted, soGetNewValue()correctly returnsnull: https://github.com/aspnetboilerplate/aspnetboilerplate/blob/06fc849206855c9e2b8cffaa3dc9200edd9fffe2/src/Abp.ZeroCore.EntityFrameworkCore/EntityHistory/Extensions/PropertyEntryExtensions.cs#L10-L15Then,
base.SaveChanges()runs. The owned entity's state becomesDetached.Then,
EntityHistoryHelper.SaveAsync(changeSet)calls the privateUpdateChangeSet(changeSet)method, which re-reads the new value: https://github.com/aspnetboilerplate/aspnetboilerplate/blob/06fc849206855c9e2b8cffaa3dc9200edd9fffe2/src/Abp.ZeroCore.EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs#L331-L336At this point, the owned entity's state is
Detached, soGetNewValue()returnspropertyEntry.CurrentValue. This makesNewValue == OriginalValue, and the property change is pruned as "unchanged".
Source: aspnetboilerplate/aspnetboilerplate