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 64b63f165e..4b993825de 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -358,6 +358,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/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs index 74f0831be2..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,7 +50,7 @@ public class AbpEfCoreNavigationHelper : ITransientDependency var index = 0; foreach (var navigationEntry in entityEntry.Navigations.Where(navigation => !navigation.IsModified)) { - if (!navigationEntry.IsLoaded) + if (!navigationEntry.IsLoaded && navigationEntry.CurrentValue == null) { index++; continue; 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(); + } +} 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 () => { 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); }); }