diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs
index f87a05e4a9..75e82976b4 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
+using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -108,12 +109,17 @@ public class EntityHistoryHelper : IEntityHistoryHelper, ITransientDependency
}
var entityType = entity.GetType();
+ var entityFullName = entityType.FullName!;
+ if (entityEntry.Metadata.HasSharedClrType && !entityEntry.Metadata.IsOwned())
+ {
+ entityFullName = entityEntry.Metadata.Name;
+ }
var entityChange = new EntityChangeInfo
{
ChangeType = changeType,
EntityEntry = entityEntry,
EntityId = entityId,
- EntityTypeFullName = entityType.FullName,
+ EntityTypeFullName = entityFullName,
PropertyChanges = GetPropertyChanges(entityEntry),
EntityTenantId = GetTenantId(entity)
};
@@ -181,48 +187,102 @@ public class EntityHistoryHelper : IEntityHistoryHelper, ITransientDependency
foreach (var property in properties)
{
+ if (entityEntry.Metadata.IsMappedToJson() && property.GetJsonPropertyName() == null)
+ {
+ continue;
+ }
+
var propertyEntry = entityEntry.Property(property.Name);
if (ShouldSavePropertyHistory(propertyEntry, isCreated || isDeleted) && !IsSoftDeleted(entityEntry))
{
+ var propertyType = DeterminePropertyTypeFromEntry(property, propertyEntry);
+
propertyChanges.Add(new EntityPropertyChangeInfo
{
NewValue = isDeleted ? null : JsonSerializer.Serialize(propertyEntry.CurrentValue!).TruncateWithPostfix(EntityPropertyChangeInfo.MaxValueLength),
OriginalValue = isCreated ? null : JsonSerializer.Serialize(propertyEntry.OriginalValue!).TruncateWithPostfix(EntityPropertyChangeInfo.MaxValueLength),
PropertyName = property.Name,
- PropertyTypeFullName = property.ClrType.GetFirstGenericArgumentIfNullable().FullName!
+ PropertyTypeFullName = propertyType.FullName!
});
}
}
- if (AbpEfCoreNavigationHelper != null)
+ if (AbpEfCoreNavigationHelper == null)
+ {
+ return propertyChanges;
+ }
+
+ foreach (var (navigationEntry, index) in entityEntry.Navigations.Select((value, i) => ( value, i )))
{
- foreach (var (navigationEntry, index) in entityEntry.Navigations.Select((value, i) => ( value, i )))
+ var propertyInfo = navigationEntry.Metadata.PropertyInfo;
+ if (propertyInfo != null &&
+ propertyInfo.IsDefined(typeof(DisableAuditingAttribute), true))
{
- var propertyInfo = navigationEntry.Metadata.PropertyInfo;
- if (propertyInfo != null &&
- propertyInfo.IsDefined(typeof(DisableAuditingAttribute), true))
+ continue;
+ }
+
+ if (navigationEntry.Metadata.TargetEntityType.IsMappedToJson() && navigationEntry is ReferenceEntry referenceEntry && referenceEntry.TargetEntry != null)
+ {
+ foreach (var propertyChange in GetPropertyChanges(referenceEntry.TargetEntry))
{
- continue;
+ propertyChange.PropertyName = $"{referenceEntry.Metadata.Name}.{propertyChange.PropertyName}";
+ propertyChanges.Add(propertyChange);
}
- if (AbpEfCoreNavigationHelper.IsNavigationEntryModified(entityEntry, index))
+ continue;
+ }
+
+ if (AbpEfCoreNavigationHelper.IsNavigationEntryModified(entityEntry, index))
+ {
+ var abpNavigationEntry = AbpEfCoreNavigationHelper.GetNavigationEntry(entityEntry, index);
+
+ var isCollection = navigationEntry.Metadata.IsCollection;
+ propertyChanges.Add(new EntityPropertyChangeInfo
{
- var abpNavigationEntry = AbpEfCoreNavigationHelper.GetNavigationEntry(entityEntry, index);
- var isCollection = navigationEntry.Metadata.IsCollection;
- propertyChanges.Add(new EntityPropertyChangeInfo
- {
- PropertyName = navigationEntry.Metadata.Name,
- PropertyTypeFullName = navigationEntry.Metadata.ClrType.GetFirstGenericArgumentIfNullable().FullName!,
- OriginalValue = GetNavigationPropertyValue(abpNavigationEntry?.OriginalValue, isCollection),
- NewValue = GetNavigationPropertyValue(abpNavigationEntry?.CurrentValue, isCollection)
- });
- }
+ PropertyName = navigationEntry.Metadata.Name,
+ PropertyTypeFullName = navigationEntry.Metadata.ClrType.GetFirstGenericArgumentIfNullable().FullName!,
+ OriginalValue = GetNavigationPropertyValue(abpNavigationEntry?.OriginalValue, isCollection),
+ NewValue = GetNavigationPropertyValue(abpNavigationEntry?.CurrentValue, isCollection)
+ });
}
}
return propertyChanges;
}
+ ///
+ /// Determines the CLR type of a property based on its EF Core metadata and the values in the given .
+ ///
+ /// The EF Core property metadata that provides the declared CLR type.
+ /// The property entry that contains the current and original values for the property.
+ ///
+ /// The most specific CLR type inferred for the property. This is normally the property's declared CLR type (with
+ /// nullable wrappers removed). If the declared type is , the type is inferred from the
+ /// runtime type of or, if that is null, from
+ /// . If both values are null, the declared CLR type
+ /// (which may remain ) is returned.
+ ///
+ protected virtual Type DeterminePropertyTypeFromEntry(IProperty property, PropertyEntry propertyEntry)
+ {
+ var propertyType = property.ClrType.GetFirstGenericArgumentIfNullable();
+
+ if (propertyType != typeof(object))
+ {
+ return propertyType;
+ }
+
+ if (propertyEntry.CurrentValue != null)
+ {
+ propertyType = propertyEntry.CurrentValue.GetType().GetFirstGenericArgumentIfNullable();
+ }
+ else if (propertyEntry.OriginalValue != null)
+ {
+ propertyType = propertyEntry.OriginalValue.GetType().GetFirstGenericArgumentIfNullable();
+ }
+
+ return propertyType;
+ }
+
protected virtual string? GetNavigationPropertyValue(object? entity, bool isCollection)
{
switch (entity)
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs
index 4e14dbc262..800b73544d 100644
--- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs
@@ -59,6 +59,8 @@ public class AbpAuditingTestModule : AbpModule
"AppEntityWithValueObject",
type => type == typeof(AppEntityWithValueObject) || type == typeof(AppEntityWithValueObjectAddress))
);
+
+ options.EntityHistorySelectors.Add(new NamedTypeSelector(nameof(AppEntityWithJsonProperty), type => type == typeof(AppEntityWithJsonProperty)));
});
context.Services.AddType();
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithJsonProperty.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithJsonProperty.cs
new file mode 100644
index 0000000000..de07544629
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithJsonProperty.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using Volo.Abp.Domain.Entities.Auditing;
+
+namespace Volo.Abp.Auditing.App.Entities;
+
+public class AppEntityWithJsonProperty : FullAuditedAggregateRoot
+{
+ public string Name { get; set; }
+
+ public JsonPropertyObject Data { get; set; }
+
+ public int Count { get; set; }
+
+ public AppEntityWithJsonProperty()
+ {
+ }
+
+ public AppEntityWithJsonProperty(Guid id, string name) : base(id)
+ {
+ Name = name;
+ }
+}
+
+public class JsonPropertyObject : Dictionary
+{
+}
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs
index 91698ea5ec..e8950880d7 100644
--- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs
@@ -30,6 +30,7 @@ public class AbpAuditingTestDbContext : AbpDbContext
public DbSet AppEntityWithNavigations { get; set; }
public DbSet AppEntityWithNavigationChildOneToMany { get; set; }
public DbSet AppEntityWithNavigationsAndDisableAuditing { get; set; }
+ public DbSet EntitiesWithObjectProperty { get; set; }
public AbpAuditingTestDbContext(DbContextOptions options)
: base(options)
@@ -56,5 +57,25 @@ public class AbpAuditingTestDbContext : AbpDbContext
b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity();
});
+ modelBuilder.Entity(b =>
+ {
+ b.ConfigureByConvention();
+ b.OwnsOne(x => x.Data, b2 =>
+ {
+ b2.ToJson();
+
+ b2.Property