Browse Source

Merge pull request #22762 from abpframework/auto-merge/rel-9-2/3688

Merge branch dev with rel-9.2
pull/22776/head
maliming 10 months ago
committed by GitHub
parent
commit
73c657ad51
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 10
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs
  2. 17
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  3. 131
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs

10
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/AbpEntityChangeOptions.cs

@ -10,8 +10,18 @@ public class AbpEntityChangeOptions
public IEntitySelectorList IgnoredNavigationEntitySelectors { get; set; }
/// <summary>
/// Default: true.
/// Update the aggregate root when any navigation property changes.
/// Some properties like ConcurrencyStamp,LastModificationTime,LastModifierId etc. will be updated.
/// </summary>
public bool UpdateAggregateRootWhenNavigationChanges { get; set; } = true;
public IEntitySelectorList IgnoredUpdateAggregateRootSelectors { get; set; }
public AbpEntityChangeOptions()
{
IgnoredNavigationEntitySelectors = new EntitySelectorList();
IgnoredUpdateAggregateRootSelectors = new EntitySelectorList();
}
}

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

@ -274,7 +274,9 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
continue;
}
if (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,7 +448,12 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
EntityChangeOptions.Value.IgnoredNavigationEntitySelectors.All(selector => !selector.Predicate(entry.Entity.GetType())) &&
AbpEfCoreNavigationHelper.IsNavigationEntryModified(entry))
{
ApplyAbpConceptsForModifiedEntity(entry, true);
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<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity);
@ -478,11 +485,13 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
}
}
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges &&
EntityChangeOptions.Value.UpdateAggregateRootWhenNavigationChanges)
{
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);
}

131
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs

@ -396,3 +396,134 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase
entityUpdatedEventTriggered.ShouldBeTrue();
}
}
public abstract class AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests : EntityFrameworkCoreTestBase
{
protected readonly IRepository<AppEntityWithNavigations, Guid> AppEntityWithNavigationsRepository;
protected readonly IRepository<AppEntityWithNavigationsForeign, Guid> AppEntityWithNavigationForeignRepository;
protected readonly ILocalEventBus LocalEventBus;
protected AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests()
{
AppEntityWithNavigationsRepository = GetRequiredService<IRepository<AppEntityWithNavigations, Guid>>();
AppEntityWithNavigationForeignRepository = GetRequiredService<IRepository<AppEntityWithNavigationsForeign, Guid>>();
LocalEventBus = GetRequiredService<ILocalEventBus>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpEntityChangeOptions>(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<EntityUpdatedEventData<AppEntityWithNavigations>>(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<AppEntityWithNavigationChildOneToMany>()
{
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<AppEntityWithNavigationChildManyToMany>()
{
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<AbpEntityChangeOptions>(options =>
{
options.UpdateAggregateRootWhenNavigationChanges = false;
});
base.AfterAddApplication(services);
}
}
public class AbpEfCoreDomainEvents_IgnoredUpdateAggregateRootSelectors_Test : AbpEfCoreDomainEvents_Disable_UpdateAggregateRoot_Tests
{
protected override void AfterAddApplication(IServiceCollection services)
{
services.Configure<AbpEntityChangeOptions>(options =>
{
options.IgnoredUpdateAggregateRootSelectors.Add("AppEntityWithValueObjectAddress", x => x == typeof(AppEntityWithNavigations));
});
base.AfterAddApplication(services);
}
}

Loading…
Cancel
Save