From 0a2e8a18f16c30accc4d62b23a1808e328c62684 Mon Sep 17 00:00:00 2001 From: Masum ULU <49063256+masumulu28@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:25:45 +0300 Subject: [PATCH 1/4] Refactor router-events.service.ts --- .../src/lib/services/router-events.service.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/router-events.service.ts b/npm/ng-packs/packages/core/src/lib/services/router-events.service.ts index f41dc9bf5e..0e17b37b04 100644 --- a/npm/ng-packs/packages/core/src/lib/services/router-events.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/router-events.service.ts @@ -34,15 +34,12 @@ export class RouterEvents { } protected listenToNavigation(): void { - ( - this.router.events.pipe( - filter(e => e instanceof NavigationEvent.End && !e.url.includes('error')), - ) as Observable - ).subscribe(event => { - // It must be "NavigationTransition" but it is not exported in Angular - //https://github.com/angular/angular/blob/9c486c96827a9282cbdbff176761bc95554a260b/packages/router/src/navigation_transition.ts#L282 - - this.#previousNavigation.set(this.#currentNavigation()); + const routerEvent$ = this.router.events.pipe( + filter(e => e instanceof NavigationEvent.End && !e.url.includes('error')) + ) as Observable; + + routerEvent$.subscribe(event => { + this.#previousNavigation.set(this.currentNavigation()); this.#currentNavigation.set(event.url); }); } From 8307a6f356614077e4ac3306c2a697fb6b31e391 Mon Sep 17 00:00:00 2001 From: maliming Date: Sat, 13 Apr 2024 10:16:31 +0800 Subject: [PATCH 2/4] Skip `PublishEntityDeletedEvent/PublishEntityUpdatedEvent` if only foreign keys have changed. --- .../Volo/Abp/EntityFrameworkCore/AbpDbContext.cs | 6 ++++++ .../Abp/TestApp/Testing/DomainEvents_Tests.cs | 15 +++++++++++++++ 2 files changed, 21 insertions(+) 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 65958ab5e5..caab5aff94 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -328,6 +328,12 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, ApplyAbpConceptsForModifiedEntity(entry); if (entry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd))) { + if (entry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey())) + { + // Skip `PublishEntityDeletedEvent/PublishEntityUpdatedEvent` if only foreign keys have changed. + break; + } + if (entry.Entity is ISoftDelete && entry.Entity.As().IsDeleted) { EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity); diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs index 4f82f26d95..f51ddf2b56 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs @@ -200,6 +200,11 @@ public abstract class DomainEvents_Tests : TestAppTestBase>(data => + { + throw new Exception("Should not trigger this event"); + }); + // Test with value object entityUpdatedEventTriggered = false; await WithUnitOfWorkAsync(async () => @@ -232,6 +237,11 @@ public abstract class DomainEvents_Tests : TestAppTestBase>(data => + { + throw new Exception("Should not trigger this event"); + }); + entityUpdatedEventTriggered = false; await WithUnitOfWorkAsync(async () => { @@ -258,6 +268,11 @@ public abstract class DomainEvents_Tests : TestAppTestBase>(data => + { + throw new Exception("Should not trigger this event"); + }); + entityUpdatedEventTriggered = false; await WithUnitOfWorkAsync(async () => { From 5f56d2bb0e3e2977d17d473aaaecec933e0c87e9 Mon Sep 17 00:00:00 2001 From: maliming Date: Sat, 13 Apr 2024 16:17:33 +0800 Subject: [PATCH 3/4] Make `AbpEfCoreNavigationHelper` to support `Navigations` which `EnsureCollectionLoaded`. --- .../AbpEfCoreNavigationHelper.cs | 6 -- .../DomainEvents/DomainEvents_Tests.cs | 84 ++++++++++++++++++- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs index 74f0831be2..b7b044332b 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs @@ -50,12 +50,6 @@ public class AbpEfCoreNavigationHelper : ITransientDependency var index = 0; foreach (var navigationEntry in entityEntry.Navigations.Where(navigation => !navigation.IsModified)) { - if (!navigationEntry.IsLoaded) - { - index++; - continue; - } - var currentValue = navigationEntry.CurrentValue; if (navigationEntry.CurrentValue is ICollection collection) { 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 cb7a5522b1..978d411634 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 @@ -1,4 +1,16 @@ -using Volo.Abp.TestApp.Testing; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.EntityFrameworkCore.DependencyInjection; +using Volo.Abp.EventBus.Local; +using Volo.Abp.TestApp.Domain; +using Volo.Abp.TestApp.Testing; +using Volo.Abp.Uow; +using Xunit; namespace Volo.Abp.EntityFrameworkCore.DomainEvents; @@ -9,3 +21,73 @@ public class DomainEvents_Tests : DomainEvents_Tests { } + +public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase +{ + protected readonly IRepository AppEntityWithNavigationsRepository; + protected readonly ILocalEventBus LocalEventBus; + + public AbpEfCoreDomainEvents_Tests() + { + AppEntityWithNavigationsRepository = GetRequiredService>(); + LocalEventBus = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.Entity(opt => + { + opt.DefaultWithDetailsFunc = q => q; + }); + }); + + base.AfterAddApplication(services); + } + + [Fact] + public async Task Should_Trigger_Domain_Events_For_Aggregate_Root_When_EnsureCollectionLoaded_Navigation_Changes_Tests() + { + var entityId = Guid.NewGuid(); + + await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity") + { + OneToMany = new List() + { + new AppEntityWithNavigationChildOneToMany + { + ChildName = "ChildName1" + } + } + }); + + var entityUpdatedEventTriggered = false; + + LocalEventBus.Subscribe>(data => + { + entityUpdatedEventTriggered = !entityUpdatedEventTriggered; + return Task.CompletedTask; + }); + + using (var scope = ServiceProvider.CreateScope()) + { + var uowManager = scope.ServiceProvider.GetRequiredService(); + using (var uow = uowManager.Begin()) + { + var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId); + entity.OneToMany.ShouldBeNull(); + + await AppEntityWithNavigationsRepository.EnsureCollectionLoadedAsync(entity, x => x.OneToMany); + entity.OneToMany.ShouldNotBeNull(); + + entity.OneToMany.Clear(); + await AppEntityWithNavigationsRepository.UpdateAsync(entity); + + await uow.CompleteAsync(); + } + } + + entityUpdatedEventTriggered.ShouldBeTrue(); + } +} From 39771f44759c062999d1966153900feee4d9d947 Mon Sep 17 00:00:00 2001 From: maliming Date: Sat, 13 Apr 2024 17:01:02 +0800 Subject: [PATCH 4/4] Skip if `navigationEntry.CurrentValue` is `null`. --- .../ChangeTrackers/AbpEfCoreNavigationHelper.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs index b7b044332b..a173f4562d 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs @@ -50,6 +50,12 @@ public class AbpEfCoreNavigationHelper : ITransientDependency var index = 0; foreach (var navigationEntry in entityEntry.Navigations.Where(navigation => !navigation.IsModified)) { + if (!navigationEntry.IsLoaded && navigationEntry.CurrentValue == null) + { + index++; + continue; + } + var currentValue = navigationEntry.CurrentValue; if (navigationEntry.CurrentValue is ICollection collection) {