From b2a3d30ca2b34e284afc15b635f98141668ef5c1 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 14 Feb 2023 10:23:54 +0800 Subject: [PATCH] Make `EntityHistoryHelper` compatible `ValueObject`. Resolve #15674 --- .../Volo/Abp/Domain/Entities/EntityHelper.cs | 7 ++++ .../EntityHistory/EntityHistoryHelper.cs | 21 ++++++---- .../Abp/Auditing/AbpAuditingTestModule.cs | 6 +++ .../App/Entities/AppEntityWithValueObject.cs | 41 +++++++++++++++++++ .../AbpAuditingTestDbContext.cs | 14 +++++++ .../Volo/Abp/Auditing/Auditing_Tests.cs | 37 +++++++++++++++++ 6 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithValueObject.cs diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs index dfc293cf13..03b192c851 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; using JetBrains.Annotations; +using Volo.Abp.Domain.Values; using Volo.Abp.MultiTenancy; using Volo.Abp.Reflection; @@ -122,6 +123,12 @@ public static class EntityHelper return typeof(IEntity).IsAssignableFrom(type); } + public static bool IsValueObject([NotNull] Type type) + { + Check.NotNull(type, nameof(type)); + return typeof(ValueObject).IsAssignableFrom(type); + } + public static void CheckEntity([NotNull] Type type) { Check.NotNull(type, nameof(type)); 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 ad901ee9f6..c887175655 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 @@ -12,6 +12,7 @@ using Volo.Abp.Auditing; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Values; using Volo.Abp.Json; using Volo.Abp.MultiTenancy; using Volo.Abp.Reflection; @@ -92,7 +93,7 @@ public class EntityHistoryHelper : IEntityHistoryHelper, ITransientDependency } var entityId = GetEntityId(entity); - if (entityId == null && changeType != EntityChangeType.Created) + if (entityId == null && changeType != EntityChangeType.Created && !(entity is ValueObject)) { return null; } @@ -139,18 +140,24 @@ public class EntityHistoryHelper : IEntityHistoryHelper, ITransientDependency protected virtual string GetEntityId(object entityAsObj) { - if (!(entityAsObj is IEntity entity)) + if ((entityAsObj is IEntity entity)) { - throw new AbpException($"Entities should implement the {typeof(IEntity).AssemblyQualifiedName} interface! Given entity does not implement it: {entityAsObj.GetType().AssemblyQualifiedName}"); + var keys = entity.GetKeys(); + if (keys.All(k => k == null)) + { + return null; + } + + return keys.JoinAsString(","); } - var keys = entity.GetKeys(); - if (keys.All(k => k == null)) + if (entityAsObj is ValueObject) { return null; } - return keys.JoinAsString(","); + throw new AbpException($"Entities should implement the {typeof(IEntity).AssemblyQualifiedName} interface or {typeof(ValueObject).AssemblyQualifiedName} class! " + + $"Given entity does not implement it: {entityAsObj.GetType().AssemblyQualifiedName}"); } /// @@ -207,7 +214,7 @@ public class EntityHistoryHelper : IEntityHistoryHelper, ITransientDependency var entityType = entityEntry.Metadata.ClrType; - if (!EntityHelper.IsEntity(entityType)) + if (!EntityHelper.IsEntity(entityType) && !EntityHelper.IsValueObject(entityType)) { return false; } 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 fd4e458410..3a21cfaf5a 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 @@ -49,6 +49,12 @@ public class AbpAuditingTestModule : AbpModule "AppEntityWithSoftDelete", type => type == typeof(AppEntityWithSoftDelete)) ); + + options.EntityHistorySelectors.Add( + new NamedTypeSelector( + "AppEntityWithValueObject", + type => type == typeof(AppEntityWithValueObject) || type == typeof(AppEntityWithValueObjectAddress)) + ); }); context.Services.AddType(); diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithValueObject.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithValueObject.cs new file mode 100644 index 0000000000..dee49a95a6 --- /dev/null +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithValueObject.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Values; + +namespace Volo.Abp.Auditing.App.Entities; + +public class AppEntityWithValueObject : AggregateRoot +{ + protected AppEntityWithValueObject() + { + + } + + public AppEntityWithValueObject(Guid id, string name, AppEntityWithValueObjectAddress appEntityWithValueObjectAddress) + : base(id) + { + Name = name; + AppEntityWithValueObjectAddress = appEntityWithValueObjectAddress; + } + + public string Name { get; set; } + + public AppEntityWithValueObjectAddress AppEntityWithValueObjectAddress { get; set; } +} + +public class AppEntityWithValueObjectAddress : ValueObject +{ + public AppEntityWithValueObjectAddress(string country) + { + + Country = country; + } + + public string Country { get; set; } + + protected override IEnumerable GetAtomicValues() + { + yield return Country; + } +} 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 8d1681f08a..8a0a798598 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 @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Volo.Abp.Auditing.App.Entities; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Modeling; namespace Volo.Abp.Auditing.App.EntityFrameworkCore; @@ -24,9 +25,22 @@ public class AbpAuditingTestDbContext : AbpDbContext public DbSet AppEntityWithSoftDelete { get; set; } + public DbSet AppEntityWithValueObject { get; set; } + public AbpAuditingTestDbContext(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(b => + { + b.ConfigureByConvention(); + b.OwnsOne(v => v.AppEntityWithValueObjectAddress); + }); + } } diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs index 6c14d5503f..cc07d90fd2 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using NSubstitute; @@ -360,6 +361,42 @@ public class Auditing_Tests : AbpAuditingTestBase #pragma warning restore 4014 } + + [Fact] + public virtual async Task TestAsync() + { + var entityId = Guid.NewGuid(); + var entity = new AppEntityWithValueObject(entityId, "test name", new AppEntityWithValueObjectAddress("USA")); + + var repository = ServiceProvider.GetRequiredService>(); + await repository.InsertAsync(entity); + + using (var scope = _auditingManager.BeginScope()) + { + using (var uow = _unitOfWorkManager.Begin()) + { + var entity2 = await repository.GetAsync(entityId); + entity2.Name = "test name 2"; + entity2.AppEntityWithValueObjectAddress = new AppEntityWithValueObjectAddress("England"); + + await repository.UpdateAsync(entity2); + + await uow.CompleteAsync(); + await scope.SaveAsync(); + } + } + +#pragma warning disable 4014 + AuditingStore.Received().SaveAsync(Arg.Is(x => x.EntityChanges.Count == 3 && + x.EntityChanges[0].ChangeType == EntityChangeType.Created && + x.EntityChanges[0].EntityTypeFullName == typeof(AppEntityWithValueObjectAddress).FullName && + x.EntityChanges[1].ChangeType == EntityChangeType.Updated && + x.EntityChanges[1].EntityTypeFullName == typeof(AppEntityWithValueObject).FullName && + x.EntityChanges[2].ChangeType == EntityChangeType.Deleted && + x.EntityChanges[2].EntityTypeFullName == typeof(AppEntityWithValueObjectAddress).FullName)); +#pragma warning restore 4014 + + } } public class Auditing_DisableLogActionInfo_Tests : Auditing_Tests