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<Employee>
{
public void Configure(EntityTypeBuilder<Employee> 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
Employeeentity 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<EntityChange> { new EntityChange { EntityTypeFullName = typeof(Employee).FullName, PropertyChanges = new List<EntityPropertyChange> { 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
HomeAddressasEmployeeentity 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<YourDbContext> 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
Addresschanges appear underEmployee.Let me know if you need further clarification
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Hi oguzhanagir, thank you for the answer and for putting me on the right way.
I preferred to extending
EntityHistoryStoreusing a more generic method, because the entityHomeAddressis 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
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image)