diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs
index 5710bf7187..2c705a01fd 100644
--- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs
@@ -8,6 +8,8 @@ public class AbpEntityChangeOptions
///
public bool PublishEntityUpdatedEventWhenNavigationChanges { get; set; } = true;
+ public IEntitySelectorList IgnoredNavigationEntitySelectors { get; set; }
+
///
/// Default: true.
/// Update the aggregate root when any navigation property changes.
@@ -15,10 +17,11 @@ public class AbpEntityChangeOptions
///
public bool UpdateAggregateRootWhenNavigationChanges { get; set; } = true;
- public IEntitySelectorList IgnoredNavigationEntitySelectors { get; set; }
+ public IEntitySelectorList IgnoredUpdateAggregateRootSelectors { get; set; }
public AbpEntityChangeOptions()
{
IgnoredNavigationEntitySelectors = new EntitySelectorList();
+ IgnoredUpdateAggregateRootSelectors = new EntitySelectorList();
}
}
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
index 96aff4e0b0..e91d08b8c9 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
@@ -274,7 +274,9 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext,
continue;
}
- if (EntityChangeOptions.Value.UpdateAggregateRootWhenNavigationChanges && entityEntry.State == EntityState.Unchanged)
+ if (EntityChangeOptions.Value.UpdateAggregateRootWhenNavigationChanges &&
+ EntityChangeOptions.Value.IgnoredUpdateAggregateRootSelectors.All(selector => !selector.Predicate(entityEntry.Entity.GetType())) &&
+ entityEntry.State == EntityState.Unchanged)
{
ApplyAbpConceptsForModifiedEntity(entityEntry, true);
}
@@ -446,10 +448,12 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext,
EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(entry.Entity.GetType())) &&
AbpEfCoreNavigationHelper.IsNavigationEntryModified(entry))
{
- if (EntityChangeOptions.Value.UpdateAggregateRootWhenNavigationChanges)
+ if (EntityChangeOptions.Value.UpdateAggregateRootWhenNavigationChanges &&
+ EntityChangeOptions.Value.IgnoredUpdateAggregateRootSelectors.All(selector => !selector.Predicate(entry.Entity.GetType())))
{
ApplyAbpConceptsForModifiedEntity(entry, true);
}
+
if (entry.Entity is ISoftDelete && entry.Entity.As().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity);
@@ -486,7 +490,8 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext,
{
foreach (var entry in AbpEfCoreNavigationHelper.GetChangedEntityEntries()
.Where(x => x.State == EntityState.Unchanged)
- .Where(x=> EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(x.Entity.GetType()))))
+ .Where(x => EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(x.Entity.GetType())))
+ .Where(x => EntityChangeOptions.Value.IgnoredUpdateAggregateRootSelectors.All(selector => !selector.Predicate(x.Entity.GetType()))))
{
UpdateConcurrencyStamp(entry);
}
diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs
index 26be74c34d..6249d588f5 100644
--- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs
+++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs
@@ -45,111 +45,6 @@ public class AbpEntityChangeOptions_DomainEvents_IgnoreEntityChangeSelectorList_
}
}
-public class AbpEfCoreDomainEvents_UpdateAggregateRootWhenNavigationChanges_Tests : EntityFrameworkCoreTestBase
-{
- protected readonly IRepository AppEntityWithNavigationsRepository;
- protected readonly IRepository AppEntityWithNavigationForeignRepository;
- protected readonly ILocalEventBus LocalEventBus;
-
- public AbpEfCoreDomainEvents_UpdateAggregateRootWhenNavigationChanges_Tests()
- {
- AppEntityWithNavigationsRepository = GetRequiredService>();
- AppEntityWithNavigationForeignRepository = GetRequiredService>();
- LocalEventBus = GetRequiredService();
- }
-
- protected override void AfterAddApplication(IServiceCollection services)
- {
- services.Configure(options =>
- {
- options.PublishEntityUpdatedEventWhenNavigationChanges = true;
- options.UpdateAggregateRootWhenNavigationChanges = false;
- });
-
- base.AfterAddApplication(services);
- }
-
- [Fact]
- public async Task Should_Trigger_Domain_Events_But_Do_Not_Change_Aggregate_Root_When_Navigation_Changes_Tests()
- {
- var entityId = Guid.NewGuid();
-
- var newEntity = await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity"));
-
- var latestConcurrencyStamp = newEntity.ConcurrencyStamp;
- var lastModificationTime = newEntity.LastModificationTime;
-
- var entityUpdatedEventTriggered = false;
-
- LocalEventBus.Subscribe>(data =>
- {
- entityUpdatedEventTriggered = true;
-
- // The Aggregate will not be updated
- data.Entity.ConcurrencyStamp.ShouldBe(latestConcurrencyStamp);
- data.Entity.LastModificationTime.ShouldBe(lastModificationTime);
- return Task.CompletedTask;
- });
-
- // Test with value object
- entityUpdatedEventTriggered = false;
- await WithUnitOfWorkAsync(async () =>
- {
- var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
- entity.AppEntityWithValueObjectAddress = new AppEntityWithValueObjectAddress("Turkey");
- await AppEntityWithNavigationsRepository.UpdateAsync(entity);
- });
- entityUpdatedEventTriggered.ShouldBeTrue();
-
- // Test with one to one
- entityUpdatedEventTriggered = false;
- await WithUnitOfWorkAsync(async () =>
- {
- var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
- entity.OneToOne = new AppEntityWithNavigationChildOneToOne
- {
- ChildName = "ChildName"
- };
- await AppEntityWithNavigationsRepository.UpdateAsync(entity);
- });
- entityUpdatedEventTriggered.ShouldBeTrue();
-
- // Test with one to many
- entityUpdatedEventTriggered = false;
- await WithUnitOfWorkAsync(async () =>
- {
- var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
- entity.OneToMany = new List()
- {
- new AppEntityWithNavigationChildOneToMany
- {
- AppEntityWithNavigationId = entity.Id,
- ChildName = "ChildName1"
- }
- };
- await AppEntityWithNavigationsRepository.UpdateAsync(entity);
- });
- entityUpdatedEventTriggered.ShouldBeTrue();
-
- // Test with many to many
- entityUpdatedEventTriggered = false;
- await WithUnitOfWorkAsync(async () =>
- {
- var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
- entity.ManyToMany = new List()
- {
- new AppEntityWithNavigationChildManyToMany
- {
- ChildName = "ChildName1"
- }
- };
- await AppEntityWithNavigationsRepository.UpdateAsync(entity);
- });
- entityUpdatedEventTriggered.ShouldBeTrue();
- }
-
-}
-
public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase
{
protected readonly IRepository AppEntityWithNavigationsRepository;
@@ -501,3 +396,134 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase
entityUpdatedEventTriggered.ShouldBeTrue();
}
}
+
+
+public abstract class AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests : EntityFrameworkCoreTestBase
+{
+ protected readonly IRepository AppEntityWithNavigationsRepository;
+ protected readonly IRepository AppEntityWithNavigationForeignRepository;
+ protected readonly ILocalEventBus LocalEventBus;
+
+ protected AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests()
+ {
+ AppEntityWithNavigationsRepository = GetRequiredService>();
+ AppEntityWithNavigationForeignRepository = GetRequiredService>();
+ LocalEventBus = GetRequiredService();
+ }
+
+ protected override void AfterAddApplication(IServiceCollection services)
+ {
+ services.Configure(options =>
+ {
+ options.PublishEntityUpdatedEventWhenNavigationChanges = true;
+ options.UpdateAggregateRootWhenNavigationChanges = false;
+ });
+
+ base.AfterAddApplication(services);
+ }
+
+ [Fact]
+ public async Task Should_Trigger_Domain_Events_But_Do_Not_Change_Aggregate_Root_When_Navigation_Changes_Tests()
+ {
+ var entityId = Guid.NewGuid();
+
+ var newEntity = await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity"));
+
+ var latestConcurrencyStamp = newEntity.ConcurrencyStamp;
+ var lastModificationTime = newEntity.LastModificationTime;
+
+ var entityUpdatedEventTriggered = false;
+
+ LocalEventBus.Subscribe>(data =>
+ {
+ entityUpdatedEventTriggered = true;
+
+ // The Aggregate will not be updated
+ data.Entity.ConcurrencyStamp.ShouldBe(latestConcurrencyStamp);
+ data.Entity.LastModificationTime.ShouldBe(lastModificationTime);
+ return Task.CompletedTask;
+ });
+
+ // Test with value object
+ entityUpdatedEventTriggered = false;
+ await WithUnitOfWorkAsync(async () =>
+ {
+ var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
+ entity.AppEntityWithValueObjectAddress = new AppEntityWithValueObjectAddress("Turkey");
+ await AppEntityWithNavigationsRepository.UpdateAsync(entity);
+ });
+ entityUpdatedEventTriggered.ShouldBeTrue();
+
+ // Test with one to one
+ entityUpdatedEventTriggered = false;
+ await WithUnitOfWorkAsync(async () =>
+ {
+ var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
+ entity.OneToOne = new AppEntityWithNavigationChildOneToOne
+ {
+ ChildName = "ChildName"
+ };
+ await AppEntityWithNavigationsRepository.UpdateAsync(entity);
+ });
+ entityUpdatedEventTriggered.ShouldBeTrue();
+
+ // Test with one to many
+ entityUpdatedEventTriggered = false;
+ await WithUnitOfWorkAsync(async () =>
+ {
+ var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
+ entity.OneToMany = new List()
+ {
+ new AppEntityWithNavigationChildOneToMany
+ {
+ AppEntityWithNavigationId = entity.Id,
+ ChildName = "ChildName1"
+ }
+ };
+ await AppEntityWithNavigationsRepository.UpdateAsync(entity);
+ });
+ entityUpdatedEventTriggered.ShouldBeTrue();
+
+ // Test with many to many
+ entityUpdatedEventTriggered = false;
+ await WithUnitOfWorkAsync(async () =>
+ {
+ var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
+ entity.ManyToMany = new List()
+ {
+ new AppEntityWithNavigationChildManyToMany
+ {
+ ChildName = "ChildName1"
+ }
+ };
+ await AppEntityWithNavigationsRepository.UpdateAsync(entity);
+ });
+ entityUpdatedEventTriggered.ShouldBeTrue();
+ }
+}
+
+public class AbpEfCoreDomainEvents_UpdateAggregateRootWhenNavigationChanges_Tests : AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests
+{
+ protected override void AfterAddApplication(IServiceCollection services)
+ {
+ services.Configure(options =>
+ {
+ options.UpdateAggregateRootWhenNavigationChanges = false;
+ });
+
+ base.AfterAddApplication(services);
+ }
+}
+
+public class AbpEfCoreDomainEvents_IgnoredUpdateAggregateRootSelectors_Test : AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests
+{
+ protected override void AfterAddApplication(IServiceCollection services)
+ {
+ services.Configure(options =>
+ {
+ options.IgnoredUpdateAggregateRootSelectors.Add("AppEntityWithValueObjectAddress", x => x == typeof(AppEntityWithNavigations));
+ });
+
+ base.AfterAddApplication(services);
+ }
+}