Я пытаюсь реализовать журнал аудита (отслеживать, что изменилось, когда и кем) для выбора классов в Entity Framework Core.
Моя текущая реализация опирается на переопределение OnSaveChangesAsyn c:
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
var currentUserFullName = _userService.CurrentUserFullName!;
foreach (var entry in ChangeTracker.Entries<AuditableEntity>())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedBy = currentUserFullName;
entry.Entity.Created = _dateTime.Now;
break;
case EntityState.Modified:
var originalValues = new Dictionary<string, object?>();
var currentValues = new Dictionary<string, object?>();
foreach (var prop in entry.Properties.Where(p => p.IsModified))
{
var name = prop.Metadata.Name;
originalValues.Add(name, prop.OriginalValue);
currentValues.Add(name, prop.CurrentValue);
}
entry.Entity.LastModifiedBy = currentUserFullName;
entry.Entity.LastModified = _dateTime.Now;
entry.Entity.LogEntries.Add(
new EntityEvent(
_dateTime.Now,
JsonConvert.SerializeObject(originalValues),
JsonConvert.SerializeObject(currentValues),
currentUserFullName));
break;
}
}
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
Это простой, чистый и очень простой в использовании; любая сущность, для которой требуется контрольный журнал, должна только унаследовать AuditableEntity
.
Однако у этого подхода есть серьезное ограничение: он не может фиксировать изменения, внесенные в свойства навигации.
Наши сущности делают хорошее использование объектов-значений, таких как адрес электронной почты:
public class EmailAddress : ValueObjectBase
{
public string Value { get; private set; } = null!;
public static EmailAddress Create(string value)
{
if (!IsValidEmail(value))
{
throw new ArgumentException("Incorrect email address format", nameof(value));
}
return new EmailAddress {
Value = value
};
}
}
... Entity.cs
public EmailAddress Email { get; set; }
... Entity EF configuration
entity.OwnsOne(e => e.Email);
... Updating
entity.Email = EmailAddress.Create("e@mail.com");
Теперь, если пользователь изменяет адрес электронной почты этой сущности, состояние сущности никогда не изменяется на измененное. Кажется, что EF Core обрабатывает объекты ValueObject как свойства навигации, которые обрабатываются отдельно.
Поэтому я думаю, что есть несколько вариантов:
Прекратить использование объектов ValueObject в качестве свойств объекта. Мы все еще можем использовать их как параметры конструктора сущностей, но это приведет к каскадной сложности для остальной части кода. Это также уменьшит доверие к достоверности данных sh.
Прекратите использование SaveChangesAsyn c и создайте собственную обработку для аудита. Опять же, это вызовет дополнительную сложность в архитектуре и, вероятно, будет менее производительным.
Некоторое странное хакерство для ChangeTracker - это звучит рискованно, но может сработать теоретически
Что-то еще, что?