I have an entity Employee with owned entity Address
public sealed class Employee : FullAuditedEntity, IMustHaveTenant
{
...
public Address HomeAddress { get; private set; }
...
}
public class Address : ValueObject
{
public int? MunicipalityId { get; private set; }
public Municipality? Municipality { get; private set; }
[MaxLength(GeographyConsts.MaxAddressLineLength)]
public string? AddressLine { get; private set; } = string.Empty;
public int? Number { get; private set; }
...
}
public class EmployeeConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.ToTable("Employees");
builder.OwnsOne(x => x.HomeAddress);
}
}
In EntityHistory the changes of Employee HomeAddress owned entity are correctly tracked but the EntityTypeFullName is Address
not Employee
so it's difficult to get a list of all the changes about an Employee.
Am I missing something?
2 Answer(s)
-
0
Hi @kompila
Sorry for the late reply. When working with owned entities (Address inside Employee), the tracking behavior can make it challenging to group all changes under the parent entity (Employee). Owned entities, by design, track changes independently from the parent entity, which complicates the history tracking for the parent entity in ASP.NET Zero. To address this, the following suggestions are provided to ensure that changes to owned entities are properly logged under the parent entity in the entity history.
For this scenario, I have two recommendations to ensure that changes to HomeAddress are tracked under the
Employee
entity instead ofAddress
:If GetEntityTypeName does not work in your case, you can manually track entity history in the EmployeeAppService before updating the address.
Modify EmployeeAppService.cs as follows:
public async Task UpdateEmployeeAddress(UpdateEmployeeAddressDto input) { var employee = await _employeeRepository.GetAsync(input.EmployeeId); if (employee != null) { using (var uow = UnitOfWorkManager.Begin()) { var changeSet = new EntityChangeSet { EntityChanges = new List { new EntityChange { EntityTypeFullName = typeof(Employee).FullName, PropertyChanges = new List { new EntityPropertyChange { PropertyName = nameof(Employee.HomeAddress), OriginalValue = employee.HomeAddress?.ToString(), NewValue = input.NewAddress?.ToString() } } } } }; await _entityHistoryStore.SaveAsync(changeSet); employee.UpdateAddress(input.NewAddress); await uow.CompleteAsync(); } } }
This approach explicitly logs changes to
HomeAddress
asEmployee
entity changes, ensuring that they are correctly associated with the employee.
Since Abp does not provide an easy override like NormalizeEntityChange, you can modify how entity history is stored by extending
EntityHistoryStore
.public class CustomEntityHistoryStore : EntityHistoryStore { public CustomEntityHistoryStore( IDbContextProvider dbContextProvider, IUnitOfWorkManager unitOfWorkManager) : base(dbContextProvider, unitOfWorkManager) { } public override Task SaveAsync(EntityChangeSet changeSet) { foreach (var change in changeSet.Changes) { if (change.EntityTypeFullName == typeof(Address).FullName) { change.EntityTypeFullName = typeof(Employee).FullName; } } return base.SaveAsync(changeSet); } }
IocManager.Register< IEntityHistoryStore, CustomEntityHistoryStore >(DependencyLifeStyle.Transient);
This method modifies history entries before saving them, ensuring that
Address
changes appear underEmployee
.Let me know if you need further clarification
-
0
Hi oguzhanagir,
thank you for the answer and for putting me on the right way.I preferred to extending
EntityHistoryStore
using a more generic method, because the entityHomeAddress
is owned by different entities and I had the same problem with other owned entities. So this is my method:public override Task SaveAsync(EntityChangeSet changeSet) { foreach (var change in changeSet.EntityChanges) { EntityEntry entityEntry = (EntityEntry)change.EntityEntry; if (entityEntry.Metadata.IsOwned()) { var ownership = entityEntry.Metadata.FindOwnership(); if (ownership != null) { change.EntityTypeFullName = ownership.PrincipalEntityType.Name; } } } return base.SaveAsync(changeSet); }
It seems to work as I need.
Thank you