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 d28468f3067..155e639bffa 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
@@ -224,18 +224,18 @@ private bool ShouldSaveEntityHistory(EntityEntry entityEntry, bool defaultValue
return true;
}
- if (entityType.IsDefined(typeof(DisableAuditingAttribute), true))
+ if (entityEntry.Metadata.GetProperties()
+ .Any(p => p.PropertyInfo?.IsDefined(typeof(AuditedAttribute)) ?? false))
{
- return false;
+ return true;
}
- if (Options.EntityHistorySelectors.Any(selector => selector.Predicate(entityType)))
+ if (entityType.IsDefined(typeof(DisableAuditingAttribute), true))
{
- return true;
+ return false;
}
- var properties = entityEntry.Metadata.GetProperties();
- if (properties.Any(p => p.PropertyInfo?.IsDefined(typeof(AuditedAttribute)) ?? false))
+ if (Options.EntityHistorySelectors.Any(selector => selector.Predicate(entityType)))
{
return true;
}
@@ -265,8 +265,7 @@ private bool ShouldSavePropertyHistory(PropertyEntry propertyEntry, bool default
}
}
- var isModified = !(propertyEntry.OriginalValue?.Equals(propertyEntry.CurrentValue) ?? propertyEntry.CurrentValue == null);
- if (isModified)
+ if (propertyEntry.IsModified)
{
return true;
}
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj b/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj
index 1ffc11b6eaa..d7c8d84149a 100644
--- a/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj
@@ -11,10 +11,12 @@
+
+
-
+
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestBase.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestBase.cs
new file mode 100644
index 00000000000..6d61ea10e54
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestBase.cs
@@ -0,0 +1,12 @@
+using Volo.Abp.Testing;
+
+namespace Volo.Abp.Auditing
+{
+ public class AbpAuditingTestBase : AbpIntegratedTest
+ {
+ protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
+ {
+ options.UseAutofac();
+ }
+ }
+}
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
new file mode 100644
index 00000000000..077658ab830
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs
@@ -0,0 +1,64 @@
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Auditing.App.Entities;
+using Volo.Abp.Auditing.App.EntityFrameworkCore;
+using Volo.Abp.Autofac;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.Auditing
+{
+ [DependsOn(
+ typeof(AbpTestBaseModule),
+ typeof(AbpAutofacModule),
+ typeof(AbpEntityFrameworkCoreModule)
+ )]
+ public class AbpAuditingTestModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAbpDbContext(options =>
+ {
+ options.AddDefaultRepositories(true);
+ });
+
+ var sqliteConnection = CreateDatabaseAndGetConnection();
+
+ Configure(options =>
+ {
+ options.Configure(abpDbContextConfigurationContext =>
+ {
+ abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection);
+ });
+ });
+
+ Configure(options =>
+ {
+ options.EntityHistorySelectors.Add(
+ new NamedTypeSelector(
+ "AppEntityWithSelector",
+ type => type == typeof(AppEntityWithSelector))
+ );
+ });
+
+ context.Services.AddType();
+ }
+
+ private static SqliteConnection CreateDatabaseAndGetConnection()
+ {
+ var connection = new SqliteConnection("Data Source=:memory:");
+ connection.Open();
+
+ using (var context = new AbpAuditingTestDbContext(new DbContextOptionsBuilder()
+ .UseSqlite(connection).Options))
+ {
+ context.GetService().CreateTables();
+ }
+
+ return connection;
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAudited.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAudited.cs
new file mode 100644
index 00000000000..6c206aea547
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAudited.cs
@@ -0,0 +1,22 @@
+using System;
+using Volo.Abp.Domain.Entities;
+
+namespace Volo.Abp.Auditing.App.Entities
+{
+ [Audited]
+ public class AppEntityWithAudited : AggregateRoot
+ {
+ protected AppEntityWithAudited()
+ {
+
+ }
+
+ public AppEntityWithAudited(Guid id, string name)
+ : base(id)
+ {
+ Name = name;
+ }
+
+ public string Name { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAuditedAndPropertyHasDisableAuditing.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAuditedAndPropertyHasDisableAuditing.cs
new file mode 100644
index 00000000000..c35372a688c
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithAuditedAndPropertyHasDisableAuditing.cs
@@ -0,0 +1,26 @@
+using System;
+using Volo.Abp.Domain.Entities;
+
+namespace Volo.Abp.Auditing.App.Entities
+{
+ [Audited]
+ public class AppEntityWithAuditedAndPropertyHasDisableAuditing : AggregateRoot
+ {
+ protected AppEntityWithAuditedAndPropertyHasDisableAuditing()
+ {
+
+ }
+
+ public AppEntityWithAuditedAndPropertyHasDisableAuditing(Guid id, string name, string name2)
+ : base(id)
+ {
+ Name = name;
+ Name2 = name2;
+ }
+
+ public string Name { get; set; }
+
+ [DisableAuditing]
+ public string Name2 { get; set; }
+ }
+}
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditing.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditing.cs
new file mode 100644
index 00000000000..446733ec982
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditing.cs
@@ -0,0 +1,22 @@
+using System;
+using Volo.Abp.Domain.Entities;
+
+namespace Volo.Abp.Auditing.App.Entities
+{
+ [DisableAuditing]
+ public class AppEntityWithDisableAuditing : AggregateRoot
+ {
+ protected AppEntityWithDisableAuditing()
+ {
+
+ }
+
+ public AppEntityWithDisableAuditing(Guid id, string name)
+ : base(id)
+ {
+ Name = name;
+ }
+
+ public string Name { get; set; }
+ }
+}
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditingAndPropertyHasAudited.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditingAndPropertyHasAudited.cs
new file mode 100644
index 00000000000..28a98bac8ef
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditingAndPropertyHasAudited.cs
@@ -0,0 +1,26 @@
+using System;
+using Volo.Abp.Domain.Entities;
+
+namespace Volo.Abp.Auditing.App.Entities
+{
+ [DisableAuditing]
+ public class AppEntityWithDisableAuditingAndPropertyHasAudited : AggregateRoot
+ {
+ protected AppEntityWithDisableAuditingAndPropertyHasAudited()
+ {
+
+ }
+
+ public AppEntityWithDisableAuditingAndPropertyHasAudited(Guid id, string name, string name2)
+ : base(id)
+ {
+ Name = name;
+ Name2 = name2;
+ }
+
+ [Audited]
+ public string Name { get; set; }
+
+ public string Name2 { get; set; }
+ }
+}
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithPropertyHasAudited.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithPropertyHasAudited.cs
new file mode 100644
index 00000000000..53207cc27cd
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithPropertyHasAudited.cs
@@ -0,0 +1,22 @@
+using System;
+using Volo.Abp.Domain.Entities;
+
+namespace Volo.Abp.Auditing.App.Entities
+{
+ public class AppEntityWithPropertyHasAudited : AggregateRoot
+ {
+ protected AppEntityWithPropertyHasAudited()
+ {
+
+ }
+
+ public AppEntityWithPropertyHasAudited(Guid id, string name)
+ : base(id)
+ {
+ Name = name;
+ }
+
+ [Audited]
+ public string Name { get; set; }
+ }
+}
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithSelector.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithSelector.cs
new file mode 100644
index 00000000000..6c4269a8c2d
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithSelector.cs
@@ -0,0 +1,16 @@
+using System;
+using Volo.Abp.Domain.Entities;
+
+namespace Volo.Abp.Auditing.App.Entities
+{
+ public class AppEntityWithSelector : AggregateRoot
+ {
+ public AppEntityWithSelector(Guid id, string name)
+ : base(id)
+ {
+ Name = name;
+ }
+
+ public string Name { get; set; }
+ }
+}
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
new file mode 100644
index 00000000000..0968bf2cc2a
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/EntityFrameworkCore/AbpAuditingTestDbContext.cs
@@ -0,0 +1,27 @@
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp.Auditing.App.Entities;
+using Volo.Abp.EntityFrameworkCore;
+
+namespace Volo.Abp.Auditing.App.EntityFrameworkCore
+{
+ public class AbpAuditingTestDbContext : AbpDbContext
+ {
+ public DbSet AppEntityWithAudited { get; set; }
+
+ public DbSet AppEntityWithAuditedAndPropertyHasDisableAuditing { get; set; }
+
+ public DbSet AppEntityWithDisableAuditing { get; set; }
+
+ public DbSet AppEntityWithDisableAuditingAndPropertyHasAudited { get; set; }
+
+ public DbSet AppEntityWithPropertyHasAudited { get; set; }
+
+ public DbSet AppEntityWithSelector { get; set; }
+
+ public AbpAuditingTestDbContext(DbContextOptions options)
+ : base(options)
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditingInterceptor_Tests.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditingInterceptor_Tests.cs
deleted file mode 100644
index a6fd566df73..00000000000
--- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditingInterceptor_Tests.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using NSubstitute;
-using Volo.Abp.Autofac;
-using Volo.Abp.DependencyInjection;
-using Volo.Abp.Modularity;
-using Volo.Abp.Testing;
-using Xunit;
-
-namespace Volo.Abp.Auditing
-{
- public class AuditingInterceptor_Tests : AbpIntegratedTest
- {
- private IAuditingStore _auditingStore;
- private IAuditingManager _auditingManager;
-
- public AuditingInterceptor_Tests()
- {
- _auditingManager = GetRequiredService();
- }
-
- protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
- {
- options.UseAutofac();
- }
-
- protected override void AfterAddApplication(IServiceCollection services)
- {
- _auditingStore = Substitute.For();
- services.Replace(ServiceDescriptor.Singleton(_auditingStore));
- }
-
- [Fact]
- public async Task Should_Write_AuditLog_For_Classes_That_Implement_IAuditingEnabled()
- {
- var myAuditedObject1 = GetRequiredService();
-
- using (var scope = _auditingManager.BeginScope())
- {
- await myAuditedObject1.DoItAsync(new InputObject { Value1 = "forty-two", Value2 = 42 });
- await scope.SaveAsync();
- }
-
-#pragma warning disable 4014
- _auditingStore.Received().SaveAsync(Arg.Any());
-#pragma warning restore 4014
- }
-
- [DependsOn(
- typeof(AbpAuditingModule),
- typeof(AbpAutofacModule)
- )]
- public class TestModule : AbpModule
- {
- public override void ConfigureServices(ServiceConfigurationContext context)
- {
- context.Services.AddType();
- }
- }
-
- public interface IMyAuditedObject : ITransientDependency, IAuditingEnabled
- {
-
- }
-
- public class MyAuditedObject1 : IMyAuditedObject
- {
- public async virtual Task DoItAsync(InputObject inputObject)
- {
- return new ResultObject
- {
- Value1 = inputObject.Value1 + "-result",
- Value2 = inputObject.Value2 + 1
- };
- }
- }
-
- public class ResultObject
- {
- public string Value1 { get; set; }
-
- public int Value2 { get; set; }
- }
-
- public class InputObject
- {
- public string Value1 { get; set; }
-
- public int Value2 { get; set; }
- }
- }
-}
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
new file mode 100644
index 00000000000..bc41f3bcded
--- /dev/null
+++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using NSubstitute;
+using Volo.Abp.Auditing.App.Entities;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Domain.Repositories;
+using Xunit;
+
+namespace Volo.Abp.Auditing
+{
+ public class Auditing_Tests : AbpAuditingTestBase
+ {
+ private IAuditingStore _auditingStore;
+ private IAuditingManager _auditingManager;
+
+ public Auditing_Tests()
+ {
+ _auditingManager = GetRequiredService();
+ }
+
+ protected override void AfterAddApplication(IServiceCollection services)
+ {
+ _auditingStore = Substitute.For();
+ services.Replace(ServiceDescriptor.Singleton(_auditingStore));
+ }
+
+ [Fact]
+ public async Task Should_Write_AuditLog_For_Classes_That_Implement_IAuditingEnabled()
+ {
+ var myAuditedObject1 = GetRequiredService();
+
+ using (var scope = _auditingManager.BeginScope())
+ {
+ await myAuditedObject1.DoItAsync(new InputObject { Value1 = "forty-two", Value2 = 42 }).ConfigureAwait(false);
+ await scope.SaveAsync().ConfigureAwait(false);
+ }
+
+#pragma warning disable 4014
+ _auditingStore.Received().SaveAsync(Arg.Any());
+#pragma warning restore 4014
+ }
+
+ public interface IMyAuditedObject : ITransientDependency, IAuditingEnabled
+ {
+
+ }
+
+ public class MyAuditedObject1 : IMyAuditedObject
+ {
+ public async virtual Task DoItAsync(InputObject inputObject)
+ {
+ return new ResultObject
+ {
+ Value1 = inputObject.Value1 + "-result",
+ Value2 = inputObject.Value2 + 1
+ };
+ }
+ }
+
+ public class ResultObject
+ {
+ public string Value1 { get; set; }
+
+ public int Value2 { get; set; }
+ }
+
+ public class InputObject
+ {
+ public string Value1 { get; set; }
+
+ public int Value2 { get; set; }
+ }
+
+ [Fact]
+ public virtual async Task Should_Write_AuditLog_For_Entity_That_Has_Audited_Attribute()
+ {
+ using (var scope = _auditingManager.BeginScope())
+ {
+ var repository = ServiceProvider.GetRequiredService>();
+ await repository.InsertAsync(new AppEntityWithAudited(Guid.NewGuid(), "test name")).ConfigureAwait(false);
+ await scope.SaveAsync().ConfigureAwait(false);
+ }
+
+#pragma warning disable 4014
+ _auditingStore.Received().SaveAsync(Arg.Any());
+#pragma warning restore 4014
+ }
+
+ [Fact]
+ public virtual async Task Should_Not_Write_AuditLog_For_Property_That_Has_DisableAuditing_Attribute()
+ {
+ using (var scope = _auditingManager.BeginScope())
+ {
+ var repository = ServiceProvider.GetRequiredService>();
+ await repository.InsertAsync(new AppEntityWithAuditedAndPropertyHasDisableAuditing(Guid.NewGuid(), "test name", "test name2")).ConfigureAwait(false);
+ await scope.SaveAsync().ConfigureAwait(false);
+ }
+
+#pragma warning disable 4014
+ _auditingStore.Received().SaveAsync(Arg.Is(x =>
+ x.EntityChanges.Count == 1 &&
+ !(x.EntityChanges[0].PropertyChanges.Any(p =>
+ p.PropertyName == nameof(AppEntityWithDisableAuditingAndPropertyHasAudited.Name2)))));
+#pragma warning restore 4014
+ }
+
+ [Fact]
+ public virtual async Task Should_Not_Write_AuditLog_For_Entity_That_Has_DisableAuditing_Attribute()
+ {
+ using (var scope = _auditingManager.BeginScope())
+ {
+ var repository = ServiceProvider.GetRequiredService>();
+ await repository.InsertAsync(new AppEntityWithDisableAuditing(Guid.NewGuid(), "test name")).ConfigureAwait(false);
+ await scope.SaveAsync().ConfigureAwait(false);
+ }
+
+#pragma warning disable 4014
+ _auditingStore.DidNotReceive().SaveAsync(Arg.Any());
+#pragma warning restore 4014
+ }
+
+ [Fact]
+ public virtual async Task Should_Write_AuditLog_For_Entity_That_Meet_Selectors()
+ {
+ using (var scope = _auditingManager.BeginScope())
+ {
+ var repository = ServiceProvider.GetRequiredService>();
+ await repository.InsertAsync(new AppEntityWithSelector(Guid.NewGuid(), "test name")).ConfigureAwait(false);
+ await scope.SaveAsync().ConfigureAwait(false);
+ }
+
+#pragma warning disable 4014
+ _auditingStore.Received().SaveAsync(Arg.Any());
+#pragma warning restore 4014
+ }
+
+ [Fact]
+ public virtual async Task Should_Write_AuditLog_For_Entity_That_Property_Has_Audited_Attribute()
+ {
+ using (var scope = _auditingManager.BeginScope())
+ {
+ var repository = ServiceProvider.GetRequiredService>();
+ await repository.InsertAsync(new AppEntityWithPropertyHasAudited(Guid.NewGuid(), "test name")).ConfigureAwait(false);
+ await scope.SaveAsync().ConfigureAwait(false);
+ }
+
+#pragma warning disable 4014
+ _auditingStore.Received().SaveAsync(Arg.Any());
+#pragma warning restore 4014
+ }
+
+ [Fact]
+ public virtual async Task Should_Write_AuditLog_For_Entity_That_Property_Has_Audited_Attribute_Even_Entity_Has_DisableAuditing_Attribute()
+ {
+ using (var scope = _auditingManager.BeginScope())
+ {
+ var repository = ServiceProvider.GetRequiredService>();
+ await repository.InsertAsync(new AppEntityWithDisableAuditingAndPropertyHasAudited(Guid.NewGuid(), "test name", "test name2")).ConfigureAwait(false);
+ await scope.SaveAsync().ConfigureAwait(false);
+ }
+
+#pragma warning disable 4014
+ _auditingStore.Received().SaveAsync(Arg.Is(x =>
+ x.EntityChanges.Count == 1 && x.EntityChanges[0].PropertyChanges.Count == 1 &&
+ x.EntityChanges[0].PropertyChanges[0].PropertyName ==
+ nameof(AppEntityWithDisableAuditingAndPropertyHasAudited.Name)));
+#pragma warning restore 4014
+ }
+ }
+}