Browse Source

`PublishEntityUpdatedEvent` when foreign key and property are changed.

Resolve #19895
pull/19910/head
maliming 2 years ago
parent
commit
150282386e
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 21
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  2. 8
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs
  3. 8
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs
  4. 19
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs
  5. 101
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs

21
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -192,9 +192,9 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
{ {
try try
{ {
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges) foreach (var entityEntry in AbpEfCoreNavigationHelper.GetChangedEntityEntries())
{ {
foreach (var entityEntry in AbpEfCoreNavigationHelper.GetChangedEntityEntries()) if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{ {
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted) if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{ {
@ -205,6 +205,23 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity); EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
} }
} }
else if (entityEntry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd)))
{
if (entityEntry.Properties.Where(x => x.IsModified).All(x => x.Metadata.IsForeignKey()))
{
// Skip `PublishEntityDeletedEvent/PublishEntityUpdatedEvent` if only foreign keys have changed.
break;
}
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
} }
var auditLog = AuditingManager?.Current?.Log; var auditLog = AuditingManager?.Current?.Log;

8
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs

@ -28,6 +28,8 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext>
public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; } public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; }
public DbSet<AppEntityWithNavigationsForeign> AppEntityWithNavigationsForeign { get; set; }
public TestMigrationsDbContext(DbContextOptions<TestMigrationsDbContext> options) public TestMigrationsDbContext(DbContextOptions<TestMigrationsDbContext> options)
: base(options) : base(options)
{ {
@ -74,6 +76,7 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext>
b.HasOne(x => x.OneToOne).WithOne().HasForeignKey<AppEntityWithNavigationChildOneToOne>(x => x.Id); b.HasOne(x => x.OneToOne).WithOne().HasForeignKey<AppEntityWithNavigationChildOneToOne>(x => x.Id);
b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationId); b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationId);
b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>(); b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>();
b.HasOne<AppEntityWithNavigationsForeign>().WithMany().HasForeignKey(x => x.AppEntityWithNavigationForeignId).IsRequired(false);
}); });
modelBuilder.Entity<AppEntityWithNavigationChildOneToOne>(b => modelBuilder.Entity<AppEntityWithNavigationChildOneToOne>(b =>
@ -87,5 +90,10 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext>
b.ConfigureByConvention(); b.ConfigureByConvention();
b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationChildOneToManyId); b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationChildOneToManyId);
}); });
modelBuilder.Entity<AppEntityWithNavigationsForeign>(b =>
{
b.ConfigureByConvention();
});
} }
} }

8
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs

@ -36,6 +36,8 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext,
public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; } public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; }
public DbSet<AppEntityWithNavigationsForeign> AppEntityWithNavigationsForeign { get; set; }
public TestAppDbContext(DbContextOptions<TestAppDbContext> options) public TestAppDbContext(DbContextOptions<TestAppDbContext> options)
: base(options) : base(options)
{ {
@ -101,6 +103,7 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext,
b.HasOne(x => x.OneToOne).WithOne().HasForeignKey<AppEntityWithNavigationChildOneToOne>(x => x.Id); b.HasOne(x => x.OneToOne).WithOne().HasForeignKey<AppEntityWithNavigationChildOneToOne>(x => x.Id);
b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationId); b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationId);
b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>(); b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>();
b.HasOne<AppEntityWithNavigationsForeign>().WithMany().HasForeignKey(x => x.AppEntityWithNavigationForeignId).IsRequired(false);
}); });
modelBuilder.Entity<AppEntityWithNavigationChildOneToOne>(b => modelBuilder.Entity<AppEntityWithNavigationChildOneToOne>(b =>
@ -115,6 +118,11 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext,
b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationChildOneToManyId); b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationChildOneToManyId);
}); });
modelBuilder.Entity<AppEntityWithNavigationsForeign>(b =>
{
b.ConfigureByConvention();
});
modelBuilder.TryConfigureObjectExtensions<TestAppDbContext>(); modelBuilder.TryConfigureObjectExtensions<TestAppDbContext>();
} }
} }

19
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs

@ -23,6 +23,7 @@ public class AppEntityWithNavigations : AggregateRoot<Guid>
public string FullName { get; set; } public string FullName { get; set; }
public AppEntityWithValueObjectAddress AppEntityWithValueObjectAddress { get; set; } public AppEntityWithValueObjectAddress AppEntityWithValueObjectAddress { get; set; }
public virtual AppEntityWithNavigationChildOneToOne OneToOne { get; set; } public virtual AppEntityWithNavigationChildOneToOne OneToOne { get; set; }
@ -30,6 +31,8 @@ public class AppEntityWithNavigations : AggregateRoot<Guid>
public virtual List<AppEntityWithNavigationChildOneToMany> OneToMany { get; set; } public virtual List<AppEntityWithNavigationChildOneToMany> OneToMany { get; set; }
public virtual List<AppEntityWithNavigationChildManyToMany> ManyToMany { get; set; } public virtual List<AppEntityWithNavigationChildManyToMany> ManyToMany { get; set; }
public virtual Guid? AppEntityWithNavigationForeignId { get; set; }
} }
public class AppEntityWithValueObjectAddress : ValueObject public class AppEntityWithValueObjectAddress : ValueObject
@ -88,3 +91,19 @@ public class AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany
public Guid AppEntityWithNavigationChildManyToManyId { get; set; } public Guid AppEntityWithNavigationChildManyToManyId { get; set; }
} }
public class AppEntityWithNavigationsForeign : AggregateRoot<Guid>
{
protected AppEntityWithNavigationsForeign()
{
}
public AppEntityWithNavigationsForeign(Guid id, string name)
: base(id)
{
Name = name;
}
public string Name { get; set; }
}

101
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs

@ -191,11 +191,13 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests<TStartupModule>
where TStartupModule : IAbpModule where TStartupModule : IAbpModule
{ {
protected readonly IRepository<AppEntityWithNavigations, Guid> AppEntityWithNavigationsRepository; protected readonly IRepository<AppEntityWithNavigations, Guid> AppEntityWithNavigationsRepository;
protected readonly IRepository<AppEntityWithNavigationsForeign, Guid> AppEntityWithNavigationForeignRepository;
protected readonly ILocalEventBus LocalEventBus; protected readonly ILocalEventBus LocalEventBus;
protected AbpEntityChangeOptions_DomainEvents_Tests() protected AbpEntityChangeOptions_DomainEvents_Tests()
{ {
AppEntityWithNavigationsRepository = GetRequiredService<IRepository<AppEntityWithNavigations, Guid>>(); AppEntityWithNavigationsRepository = GetRequiredService<IRepository<AppEntityWithNavigations, Guid>>();
AppEntityWithNavigationForeignRepository = GetRequiredService<IRepository<AppEntityWithNavigationsForeign, Guid>>();
LocalEventBus = GetRequiredService<ILocalEventBus>(); LocalEventBus = GetRequiredService<ILocalEventBus>();
} }
@ -288,4 +290,103 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests<TStartupModule>
}); });
entityUpdatedEventTriggered.ShouldBeFalse(); entityUpdatedEventTriggered.ShouldBeFalse();
} }
[Fact]
public async Task Should_Trigger_EntityUpdatedEvent_For_Aggregate_Root_When_Property_And_Navigation_Changes_Tests()
{
var entityId = Guid.NewGuid();
await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity"));
var entityWithNavigationForeignId = Guid.NewGuid();
var entityWithNavigationForeignId2 = Guid.NewGuid();
await AppEntityWithNavigationForeignRepository.InsertAsync(new AppEntityWithNavigationsForeign(entityWithNavigationForeignId, "TestEntityWithNavigationForeign"));
await AppEntityWithNavigationForeignRepository.InsertAsync(new AppEntityWithNavigationsForeign(entityWithNavigationForeignId2, "TestEntityWithNavigationForeign2"));
var entityUpdatedEventTriggered = false;
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigations>>(data =>
{
entityUpdatedEventTriggered = !entityUpdatedEventTriggered;
return Task.CompletedTask;
});
// Test with simple property with foreign key
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.Name = Guid.NewGuid().ToString();
entity.AppEntityWithNavigationForeignId = entityWithNavigationForeignId;
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
// Test only foreign key changed
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.AppEntityWithNavigationForeignId = entityWithNavigationForeignId2;
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeFalse();
// Test with simple property with value object
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.Name = Guid.NewGuid().ToString();
entity.AppEntityWithValueObjectAddress = new AppEntityWithValueObjectAddress("Turkey");
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
// Test with simple property with one to one
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.Name = Guid.NewGuid().ToString();
entity.OneToOne = new AppEntityWithNavigationChildOneToOne
{
ChildName = "ChildName"
};
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
// Test with simple property with one to many
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.Name = Guid.NewGuid().ToString();
entity.OneToMany = new List<AppEntityWithNavigationChildOneToMany>()
{
new AppEntityWithNavigationChildOneToMany
{
AppEntityWithNavigationId = entity.Id,
ChildName = "ChildName1"
}
};
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
// Test with simple property with many to many
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.Name = Guid.NewGuid().ToString();
entity.ManyToMany = new List<AppEntityWithNavigationChildManyToMany>()
{
new AppEntityWithNavigationChildManyToMany
{
ChildName = "ChildName1"
}
};
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
}
} }

Loading…
Cancel
Save