Browse Source

Merge pull request #19598 from abpframework/auto-merge/rel-8-1/2649

Merge branch dev with rel-8.1
pull/19601/head
maliming 2 years ago
committed by GitHub
parent
commit
860c32a02a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 37
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  2. 202
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs
  3. 50
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEntityEntry.cs
  4. 28
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEntityEntryNavigationProperty.cs
  5. 18
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs
  6. 2
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs
  7. 268
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/DomainEvents/DomainEvents_Tests.cs
  8. 4
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EntityFrameworkCoreTestBase.cs
  9. 14
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs
  10. 12
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs
  11. 18
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/AppEntityWithNavigations.cs
  12. 153
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs

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

@ -192,6 +192,21 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
{ {
try try
{ {
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
foreach (var entityEntry in AbpEfCoreNavigationHelper.GetChangedEntityEntries())
{
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;
List<EntityChangeInfo>? entityChangeList = null; List<EntityChangeInfo>? entityChangeList = null;
if (auditLog != null) if (auditLog != null)
@ -289,14 +304,14 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
protected virtual void ChangeTracker_Tracked(object? sender, EntityTrackedEventArgs e) protected virtual void ChangeTracker_Tracked(object? sender, EntityTrackedEventArgs e)
{ {
AbpEfCoreNavigationHelper.ChangeTracker_Tracked(ChangeTracker, sender, e); AbpEfCoreNavigationHelper.ChangeTracker_Tracked(sender, e);
FillExtraPropertiesForTrackedEntities(e); FillExtraPropertiesForTrackedEntities(e);
PublishEventsForTrackedEntity(e.Entry); PublishEventsForTrackedEntity(e.Entry);
} }
protected virtual void ChangeTracker_StateChanged(object? sender, EntityStateChangedEventArgs e) protected virtual void ChangeTracker_StateChanged(object? sender, EntityStateChangedEventArgs e)
{ {
AbpEfCoreNavigationHelper.ChangeTracker_StateChanged(ChangeTracker, sender, e); AbpEfCoreNavigationHelper.ChangeTracker_StateChanged(sender, e);
PublishEventsForTrackedEntity(e.Entry); PublishEventsForTrackedEntity(e.Entry);
} }
@ -374,8 +389,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
EntityChangeEventHelper.PublishEntityUpdatedEvent(entry.Entity); EntityChangeEventHelper.PublishEntityUpdatedEvent(entry.Entity);
} }
} }
else if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges && else if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges && AbpEfCoreNavigationHelper.IsEntityEntryModified(entry))
(entry.Navigations.Any(x => x.IsModified) || AbpEfCoreNavigationHelper.IsEntityEntryNavigationChanged(entry)))
{ {
if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted) if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
{ {
@ -393,21 +407,6 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity); EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity);
break; break;
} }
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
foreach (var entityEntry in AbpEfCoreNavigationHelper.GetChangedEntityEntries())
{
if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
}
} }
protected virtual void HandlePropertiesBeforeSave() protected virtual void HandlePropertiesBeforeSave()

202
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs

@ -1,9 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
@ -15,18 +15,18 @@ namespace Volo.Abp.EntityFrameworkCore.ChangeTrackers;
/// </summary> /// </summary>
public class AbpEfCoreNavigationHelper : ITransientDependency public class AbpEfCoreNavigationHelper : ITransientDependency
{ {
protected Dictionary<string, List<AbpEntityEntryNavigationProperty>> EntityEntryNavigationProperties { get; } = new Dictionary<string, List<AbpEntityEntryNavigationProperty>>(); protected Dictionary<string, AbpEntityEntry> EntityEntries { get; } = new();
public virtual void ChangeTracker_Tracked(ChangeTracker changeTracker, object? sender, EntityTrackedEventArgs e) public virtual void ChangeTracker_Tracked(object? sender, EntityTrackedEventArgs e)
{ {
EntityEntryTrackedOrStateChanged(e.Entry); EntityEntryTrackedOrStateChanged(e.Entry);
DetectChanges(); DetectChanges(e.Entry);
} }
public virtual void ChangeTracker_StateChanged(ChangeTracker changeTracker, object? sender, EntityStateChangedEventArgs e) public virtual void ChangeTracker_StateChanged(object? sender, EntityStateChangedEventArgs e)
{ {
EntityEntryTrackedOrStateChanged(e.Entry); EntityEntryTrackedOrStateChanged(e.Entry);
DetectChanges(); DetectChanges(e.Entry);
} }
protected virtual void EntityEntryTrackedOrStateChanged(EntityEntry entityEntry) protected virtual void EntityEntryTrackedOrStateChanged(EntityEntry entityEntry)
@ -36,156 +36,166 @@ public class AbpEfCoreNavigationHelper : ITransientDependency
return; return;
} }
var entryId = GetEntityId(entityEntry); var entryId = GetEntityEntryIdentity(entityEntry);
if (entryId == null) if (entryId == null)
{ {
return; return;
} }
var navigationProperties = EntityEntryNavigationProperties.GetOrAdd(entryId, () => new List<AbpEntityEntryNavigationProperty>()); if (EntityEntries.ContainsKey(entryId))
var index = 0;
foreach (var navigationEntry in entityEntry.Navigations.Where(navigation => !navigation.IsModified))
{ {
var navigationProperty = navigationProperties.FirstOrDefault(x => x.Index == index); return;
if (navigationProperty != null) }
EntityEntries.Add(entryId, new AbpEntityEntry(entryId, entityEntry));
}
protected virtual void DetectChanges(EntityEntry entityEntry)
{
if (entityEntry.State != EntityState.Added &&
entityEntry.State != EntityState.Deleted &&
entityEntry.State != EntityState.Modified)
{
return;
}
RecursiveDetectChanges(entityEntry);
}
protected virtual void RecursiveDetectChanges(EntityEntry entityEntry)
{
#pragma warning disable EF1001
var stateManager = entityEntry.Context.GetDependencies().StateManager;
var internalEntityEntityEntry = stateManager.Entries.FirstOrDefault(x => x.Entity == entityEntry.Entity);
if (internalEntityEntityEntry == null)
{
return;
}
var foreignKeys = entityEntry.Metadata.GetForeignKeys().ToList();
foreach (var foreignKey in foreignKeys)
{
var principal = stateManager.FindPrincipal(internalEntityEntityEntry, foreignKey);
if (principal == null)
{ {
if (navigationProperty.Value == null || IsCollectionAndEmpty(navigationProperty.Value)) continue;
{ }
navigationProperty.Value = navigationEntry.CurrentValue;
} var entryId = GetEntityEntryIdentity(principal.ToEntityEntry());
if (entryId == null || !EntityEntries.TryGetValue(entryId, out var abpEntityEntry))
{
continue;
} }
else
if (!abpEntityEntry.IsModified)
{ {
navigationProperties.Add(new AbpEntityEntryNavigationProperty(index, navigationEntry.Metadata.Name, navigationEntry.CurrentValue, false, entityEntry, navigationEntry)); abpEntityEntry.IsModified = true;
RecursiveDetectChanges(abpEntityEntry.EntityEntry);
} }
index++; var navigationEntry = abpEntityEntry.NavigationEntries.FirstOrDefault(x => x.NavigationEntry.Metadata is INavigation navigationMetadata && navigationMetadata.ForeignKey == foreignKey) ??
abpEntityEntry.NavigationEntries.FirstOrDefault(x => x.NavigationEntry.Metadata is ISkipNavigation skipNavigationMetadata && skipNavigationMetadata.ForeignKey == foreignKey);
if (navigationEntry != null)
{
navigationEntry.IsModified = true;
}
} }
}
protected virtual void DetectChanges() var skipNavigations = entityEntry.Metadata.GetSkipNavigations().ToList();
{ foreach (var skipNavigation in skipNavigations)
foreach (var entityEntryNavigationProperties in EntityEntryNavigationProperties)
{ {
foreach (var navigationProperty in entityEntryNavigationProperties.Value.Where(x => !x.IsChanged && x.EntityEntry.State == EntityState.Unchanged)) var joinEntityType = skipNavigation.JoinEntityType;
var foreignKey = skipNavigation.ForeignKey;
var inverseForeignKey = skipNavigation.Inverse.ForeignKey;
foreach (var joinEntry in stateManager.Entries)
{ {
if (navigationProperty.NavigationEntry.IsModified) if (joinEntry.EntityType != joinEntityType || stateManager.FindPrincipal(joinEntry, foreignKey) != internalEntityEntityEntry)
{ {
navigationProperty.IsChanged = true;
continue; continue;
} }
if (navigationProperty.Value == null || IsCollectionAndEmpty(navigationProperty.Value)) var principal = stateManager.FindPrincipal(joinEntry, inverseForeignKey);
if (principal == null)
{ {
if (navigationProperty.NavigationEntry.CurrentValue != null || IsCollectionAndNotEmpty(navigationProperty.NavigationEntry.CurrentValue)) continue;
{ }
if (navigationProperty.NavigationEntry.CurrentValue is ICollection collection)
{ var entryId = GetEntityEntryIdentity(principal.ToEntityEntry());
navigationProperty.Value = collection.Cast<object?>().ToList(); if (entryId == null || !EntityEntries.TryGetValue(entryId, out var abpEntityEntry))
} {
else continue;
{ }
navigationProperty.Value = navigationProperty.NavigationEntry.CurrentValue;
} if (!abpEntityEntry.IsModified)
} {
abpEntityEntry.IsModified = true;
RecursiveDetectChanges(abpEntityEntry.EntityEntry);
} }
if (navigationProperty.Value != null || IsCollectionAndNotEmpty(navigationProperty.Value)) var navigationEntry = abpEntityEntry.NavigationEntries.FirstOrDefault(x => x.NavigationEntry.Metadata is INavigation navigationMetadata && navigationMetadata.ForeignKey == inverseForeignKey) ??
abpEntityEntry.NavigationEntries.FirstOrDefault(x => x.NavigationEntry.Metadata is ISkipNavigation skipNavigationMetadata && skipNavigationMetadata.ForeignKey == inverseForeignKey);
if (navigationEntry != null)
{ {
if (navigationProperty.NavigationEntry.CurrentValue == null || IsCollectionAndEmpty(navigationProperty.NavigationEntry.CurrentValue)) navigationEntry.IsModified = true;
{
if (IsCollectionAndEmpty(navigationProperty.Value) && IsCollectionAndEmpty(navigationProperty.NavigationEntry.CurrentValue))
{
continue;
}
navigationProperty.IsChanged = true;
}
} }
} }
} }
#pragma warning restore EF1001
} }
public virtual List<EntityEntry> GetChangedEntityEntries() public virtual List<EntityEntry> GetChangedEntityEntries()
{ {
DetectChanges(); return EntityEntries
return EntityEntryNavigationProperties .Where(x => x.Value.IsModified)
.SelectMany(x => x.Value) .Select(x => x.Value.EntityEntry)
.Where(x => x.NavigationEntry.IsModified || x.IsChanged)
.Select(x => x.EntityEntry)
.ToList(); .ToList();
} }
public virtual bool IsEntityEntryNavigationChanged(EntityEntry entityEntry) public virtual bool IsEntityEntryModified(EntityEntry entityEntry)
{ {
DetectChanges();
if (entityEntry.State == EntityState.Modified) if (entityEntry.State == EntityState.Modified)
{ {
return true; return true;
} }
var entryId = GetEntityId(entityEntry); var entryId = GetEntityEntryIdentity(entityEntry);
if (entryId == null) if (entryId == null)
{ {
return false; return false;
} }
if (EntityEntryNavigationProperties.TryGetValue(entryId, out var navigationProperties)) return EntityEntries.TryGetValue(entryId, out var abpEntityEntry) && abpEntityEntry.IsModified;
{
return navigationProperties.Any(x => x.IsChanged) ||
navigationProperties.Any(x => x.NavigationEntry.IsModified) ||
navigationProperties.Any(x =>
x.NavigationEntry is ReferenceEntry &&
x.NavigationEntry.As<ReferenceEntry>().TargetEntry?.State == EntityState.Modified);
}
return false;
} }
public virtual bool IsEntityEntryNavigationChanged(NavigationEntry navigationEntry, int index) public virtual bool IsNavigationEntryModified(EntityEntry entityEntry, int navigationEntryIndex)
{ {
if (navigationEntry.IsModified || (navigationEntry is ReferenceEntry && navigationEntry.As<ReferenceEntry>().TargetEntry?.State == EntityState.Modified)) var entryId = GetEntityEntryIdentity(entityEntry);
{
return true;
}
var entryId = GetEntityId(navigationEntry.EntityEntry);
if (entryId == null) if (entryId == null)
{ {
return false; return false;
} }
if (EntityEntryNavigationProperties.TryGetValue(entryId, out var navigationProperties)) if (!EntityEntries.TryGetValue(entryId, out var abpEntityEntry))
{ {
var navigationProperty = navigationProperties.FirstOrDefault(x => x.Index == index); return false;
if (navigationProperty != null && navigationProperty.IsChanged)
{
return true;
}
} }
return false; var navigationEntryProperty = abpEntityEntry.NavigationEntries.ElementAtOrDefault(navigationEntryIndex);
return navigationEntryProperty != null && navigationEntryProperty.IsModified;
} }
public void Clear() protected virtual string? GetEntityEntryIdentity(EntityEntry entityEntry)
{ {
EntityEntryNavigationProperties.Clear(); if (entityEntry.Entity is IEntity entryEntity && entryEntity.GetKeys().Length == 1)
} {
return $"{entityEntry.Metadata.ClrType.FullName}:{entryEntity.GetKeys().FirstOrDefault()}";
private string? GetEntityId(EntityEntry entityEntry) }
{
return entityEntry.Entity is IEntity entryEntity && entryEntity.GetKeys().Length == 1
? entryEntity.GetKeys().FirstOrDefault()?.ToString()
: null;
}
private bool IsCollectionAndEmpty(object? value) return null;
{
return value is ICollection && value is ICollection collection && collection.Count == 0;
} }
private bool IsCollectionAndNotEmpty(object? value) public void Clear()
{ {
return value is ICollection && value is ICollection collection && collection.Count != 0; EntityEntries.Clear();
} }
} }

50
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEntityEntry.cs

@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace Volo.Abp.EntityFrameworkCore.ChangeTrackers;
public class AbpEntityEntry
{
public string Id { get; set; }
public EntityEntry EntityEntry { get; set; }
public List<AbpNavigationEntry> NavigationEntries { get; set; }
private bool _isModified;
public bool IsModified
{
get
{
return _isModified || EntityEntry.State == EntityState.Modified || NavigationEntries.Any(n => n.IsModified);
}
set
{
_isModified = value;
}
}
public AbpEntityEntry(string id, EntityEntry entityEntry)
{
Id = id;
EntityEntry = entityEntry;
NavigationEntries = EntityEntry.Navigations.Select(x => new AbpNavigationEntry(x, x.Metadata.Name)).ToList();
}
}
public class AbpNavigationEntry
{
public NavigationEntry NavigationEntry { get; set; }
public string Name { get; set; }
public bool IsModified { get; set; }
public AbpNavigationEntry(NavigationEntry navigationEntry, string name)
{
NavigationEntry = navigationEntry;
Name = name;
}
}

28
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEntityEntryNavigationProperty.cs

@ -1,28 +0,0 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace Volo.Abp.EntityFrameworkCore.ChangeTrackers;
public class AbpEntityEntryNavigationProperty
{
public int Index { get; set; }
public string Name { get; set; }
public object? Value { get; set; }
public bool IsChanged { get; set; }
public EntityEntry EntityEntry { get; set; }
public NavigationEntry NavigationEntry { get; set; }
public AbpEntityEntryNavigationProperty(int index, string name, object? value, bool isChanged, EntityEntry entityEntry, NavigationEntry navigationEntry)
{
Index = index;
Name = name;
Value = value;
IsChanged = isChanged;
EntityEntry = entityEntry;
NavigationEntry = navigationEntry;
}
}

18
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs

@ -193,21 +193,18 @@ public class EntityHistoryHelper : IEntityHistoryHelper, ITransientDependency
} }
} }
if (entityEntry.State == EntityState.Unchanged && Options.SaveEntityHistoryWhenNavigationChanges && AbpEfCoreNavigationHelper != null) if (Options.SaveEntityHistoryWhenNavigationChanges && AbpEfCoreNavigationHelper != null)
{ {
var index = 0; foreach (var (navigationEntry, index) in entityEntry.Navigations.Select((value, i) => ( value, i )))
foreach (var navigation in entityEntry.Navigations)
{ {
if (navigation.IsModified || AbpEfCoreNavigationHelper.IsEntityEntryNavigationChanged(navigation, index)) if (AbpEfCoreNavigationHelper.IsNavigationEntryModified(entityEntry, index))
{ {
propertyChanges.Add(new EntityPropertyChangeInfo propertyChanges.Add(new EntityPropertyChangeInfo
{ {
PropertyName = navigation.Metadata.Name, PropertyName = navigationEntry.Metadata.Name,
PropertyTypeFullName = navigation.Metadata.ClrType.GetFirstGenericArgumentIfNullable().FullName! PropertyTypeFullName = navigationEntry.Metadata.ClrType.GetFirstGenericArgumentIfNullable().FullName!
}); });
} }
index++;
} }
} }
@ -255,10 +252,9 @@ public class EntityHistoryHelper : IEntityHistoryHelper, ITransientDependency
protected virtual bool HasNavigationPropertiesChanged(EntityEntry entityEntry) protected virtual bool HasNavigationPropertiesChanged(EntityEntry entityEntry)
{ {
return entityEntry.State == EntityState.Unchanged && return Options.SaveEntityHistoryWhenNavigationChanges &&
Options.SaveEntityHistoryWhenNavigationChanges &&
AbpEfCoreNavigationHelper != null && AbpEfCoreNavigationHelper != null &&
AbpEfCoreNavigationHelper.IsEntityEntryNavigationChanged(entityEntry); AbpEfCoreNavigationHelper.IsEntityEntryModified(entityEntry);
} }
protected virtual bool ShouldSavePropertyHistory(PropertyEntry propertyEntry, bool defaultValue) protected virtual bool ShouldSavePropertyHistory(PropertyEntry propertyEntry, bool defaultValue)

2
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs

@ -51,7 +51,7 @@ public class AbpEntityFrameworkCoreTestModule : AbpModule
options.Entity<AppEntityWithNavigations>(opt => options.Entity<AppEntityWithNavigations>(opt =>
{ {
opt.DefaultWithDetailsFunc = q => q.Include(p => p.OneToOne).Include(p => p.OneToMany).Include(p => p.ManyToMany); opt.DefaultWithDetailsFunc = q => q.Include(p => p.OneToOne).ThenInclude(x => x.OneToOne).Include(p => p.OneToMany).ThenInclude(x => x.OneToMany).Include(p => p.ManyToMany);
}); });
}); });

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

@ -26,11 +26,14 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase
{ {
protected readonly IRepository<AppEntityWithNavigations, Guid> AppEntityWithNavigationsRepository; protected readonly IRepository<AppEntityWithNavigations, Guid> AppEntityWithNavigationsRepository;
protected readonly ILocalEventBus LocalEventBus; protected readonly ILocalEventBus LocalEventBus;
protected readonly IRepository<Person, Guid> PersonRepository;
protected bool _loadEntityWithDetails = false;
public AbpEfCoreDomainEvents_Tests() public AbpEfCoreDomainEvents_Tests()
{ {
AppEntityWithNavigationsRepository = GetRequiredService<IRepository<AppEntityWithNavigations, Guid>>(); AppEntityWithNavigationsRepository = GetRequiredService<IRepository<AppEntityWithNavigations, Guid>>();
LocalEventBus = GetRequiredService<ILocalEventBus>(); LocalEventBus = GetRequiredService<ILocalEventBus>();
PersonRepository = GetRequiredService<IRepository<Person, Guid>>();
} }
protected override void AfterAddApplication(IServiceCollection services) protected override void AfterAddApplication(IServiceCollection services)
@ -39,16 +42,279 @@ public class AbpEfCoreDomainEvents_Tests : EntityFrameworkCoreTestBase
{ {
options.Entity<AppEntityWithNavigations>(opt => options.Entity<AppEntityWithNavigations>(opt =>
{ {
opt.DefaultWithDetailsFunc = q => q; if (_loadEntityWithDetails)
{
opt.DefaultWithDetailsFunc = q => q;
}
}); });
}); });
base.AfterAddApplication(services); base.AfterAddApplication(services);
} }
[Fact]
public async Task Should_Trigger_Domain_Events_For_Aggregate_Root_When_Navigation_Changes_Tests()
{
_loadEntityWithDetails = false;
var entityId = Guid.NewGuid();
await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity"));
var entityUpdatedEventTriggered = false;
var personCreatedEventCount = 0;
var entityUpdatedEventTriggerCount = 0;
LocalEventBus.Subscribe<EntityCreatedEventData<Person>>(_ =>
{
personCreatedEventCount++;
return Task.CompletedTask;
});
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigations>>(async _ =>
{
entityUpdatedEventTriggered = true;
await PersonRepository.InsertAsync(new Person(Guid.NewGuid(), Guid.NewGuid().ToString(), new Random().Next(1, 100)));
});
// Test with simple property
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.Name = Guid.NewGuid().ToString();
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
// 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();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.AppEntityWithValueObjectAddress.Country = "USA";
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithValueObjectAddress>>(_ =>
{
throw new Exception("Should not trigger this event");
});
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.AppEntityWithValueObjectAddress = null;
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
// Test with one to one
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToOne = new AppEntityWithNavigationChildOneToOne
{
ChildName = "ChildName",
OneToOne = new AppEntityWithNavigationChildOneToOneAndOneToOne
{
ChildName = "OneToOne-ChildName"
}
};
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
var oneToOneEntityUpdatedEventTriggered = false;
var oneToOneAndOneToOneEntityUpdatedEventTriggered = false;
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationChildOneToOne>>(async _ =>
{
oneToOneEntityUpdatedEventTriggered = true;
});
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationChildOneToOneAndOneToOne>>(async _ =>
{
oneToOneAndOneToOneEntityUpdatedEventTriggered = true;
});
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToOne.ChildName = "ChildName2";
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
oneToOneEntityUpdatedEventTriggered.ShouldBeTrue();
oneToOneAndOneToOneEntityUpdatedEventTriggered.ShouldBeFalse();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
entityUpdatedEventTriggered = false;
oneToOneEntityUpdatedEventTriggered = false;
oneToOneAndOneToOneEntityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToOne.OneToOne.ChildName = "OneToOne-ChildName2";
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
oneToOneEntityUpdatedEventTriggered.ShouldBeTrue();
oneToOneAndOneToOneEntityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationChildOneToOne>>(_ =>
{
throw new Exception("Should not trigger this event");
});
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToOne = null;
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
// 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",
OneToMany = new List<AppEntityWithNavigationChildOneToManyAndOneToMany>()
{
new AppEntityWithNavigationChildOneToManyAndOneToMany()
{
ChildName = "OneToMany-ChildName1"
}
}
}
};
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
var oneToManyEntityUpdatedEventTriggered = false;
var oneToManyAndOneToManyEntityUpdatedEventTriggered = false;
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationChildOneToMany>>(async _ =>
{
oneToManyEntityUpdatedEventTriggered = true;
});
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationChildOneToManyAndOneToMany>>(async _ =>
{
oneToManyAndOneToManyEntityUpdatedEventTriggered = true;
});
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToMany[0].ChildName = "ChildName2";
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
oneToManyEntityUpdatedEventTriggered.ShouldBeTrue();
oneToManyAndOneToManyEntityUpdatedEventTriggered.ShouldBeFalse();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
entityUpdatedEventTriggered = false;
oneToManyEntityUpdatedEventTriggered = false;
oneToManyAndOneToManyEntityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToMany[0].OneToMany[0].ChildName = "OneToMany-ChildName2";
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
oneToManyEntityUpdatedEventTriggered.ShouldBeTrue();
oneToManyAndOneToManyEntityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationChildOneToMany>>(_ =>
{
throw new Exception("Should not trigger this event");
});
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToMany.Clear();
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
// 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();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.ManyToMany[0].ChildName = "ChildName2";
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.ManyToMany.Clear();
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
}
[Fact] [Fact]
public async Task Should_Trigger_Domain_Events_For_Aggregate_Root_When_EnsureCollectionLoaded_Navigation_Changes_Tests() public async Task Should_Trigger_Domain_Events_For_Aggregate_Root_When_EnsureCollectionLoaded_Navigation_Changes_Tests()
{ {
_loadEntityWithDetails = true;
var entityId = Guid.NewGuid(); var entityId = Guid.NewGuid();
await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity") await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity")

4
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EntityFrameworkCoreTestBase.cs

@ -1,8 +1,8 @@
using Volo.Abp.Testing; using Volo.Abp.TestApp.Testing;
namespace Volo.Abp.EntityFrameworkCore; namespace Volo.Abp.EntityFrameworkCore;
public abstract class EntityFrameworkCoreTestBase : AbpIntegratedTest<AbpEntityFrameworkCoreTestModule> public abstract class EntityFrameworkCoreTestBase : TestAppTestBase<AbpEntityFrameworkCoreTestModule>
{ {
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{ {

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

@ -70,10 +70,22 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext>
modelBuilder.Entity<AppEntityWithNavigations>(b => modelBuilder.Entity<AppEntityWithNavigations>(b =>
{ {
b.ConfigureByConvention(); b.ConfigureByConvention();
b.OwnsOne(v => v.AppEntityWithValueObjectAddress); b.OwnsOne(x => x.AppEntityWithValueObjectAddress);
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>();
}); });
modelBuilder.Entity<AppEntityWithNavigationChildOneToOne>(b =>
{
b.ConfigureByConvention();
b.HasOne(x => x.OneToOne).WithOne().HasForeignKey<AppEntityWithNavigationChildOneToOneAndOneToOne>(x => x.Id);
});
modelBuilder.Entity<AppEntityWithNavigationChildOneToMany>(b =>
{
b.ConfigureByConvention();
b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationChildOneToManyId);
});
} }
} }

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

@ -103,6 +103,18 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext,
b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>(); b.HasMany(x => x.ManyToMany).WithMany(x => x.ManyToMany).UsingEntity<AppEntityWithNavigationsAndAppEntityWithNavigationChildManyToMany>();
}); });
modelBuilder.Entity<AppEntityWithNavigationChildOneToOne>(b =>
{
b.ConfigureByConvention();
b.HasOne(x => x.OneToOne).WithOne().HasForeignKey<AppEntityWithNavigationChildOneToOneAndOneToOne>(x => x.Id);
});
modelBuilder.Entity<AppEntityWithNavigationChildOneToMany>(b =>
{
b.ConfigureByConvention();
b.HasMany(x => x.OneToMany).WithOne().HasForeignKey(x => x.AppEntityWithNavigationChildOneToManyId);
});
modelBuilder.TryConfigureObjectExtensions<TestAppDbContext>(); modelBuilder.TryConfigureObjectExtensions<TestAppDbContext>();
} }
} }

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

@ -36,7 +36,6 @@ public class AppEntityWithValueObjectAddress : ValueObject
{ {
public AppEntityWithValueObjectAddress(string country) public AppEntityWithValueObjectAddress(string country)
{ {
Country = country; Country = country;
} }
@ -48,8 +47,14 @@ public class AppEntityWithValueObjectAddress : ValueObject
} }
} }
public class AppEntityWithNavigationChildOneToOne : Entity<Guid> public class AppEntityWithNavigationChildOneToOne : Entity<Guid>
{
public string ChildName { get; set; }
public virtual AppEntityWithNavigationChildOneToOneAndOneToOne OneToOne { get; set; }
}
public class AppEntityWithNavigationChildOneToOneAndOneToOne : Entity<Guid>
{ {
public string ChildName { get; set; } public string ChildName { get; set; }
} }
@ -59,6 +64,15 @@ public class AppEntityWithNavigationChildOneToMany : Entity<Guid>
public Guid AppEntityWithNavigationId { get; set; } public Guid AppEntityWithNavigationId { get; set; }
public string ChildName { get; set; } public string ChildName { get; set; }
public virtual List<AppEntityWithNavigationChildOneToManyAndOneToMany> OneToMany { get; set; }
}
public class AppEntityWithNavigationChildOneToManyAndOneToMany : Entity<Guid>
{
public Guid AppEntityWithNavigationChildOneToManyId { get; set; }
public string ChildName { get; set; }
} }
public class AppEntityWithNavigationChildManyToMany : AggregateRoot<Guid> public class AppEntityWithNavigationChildManyToMany : AggregateRoot<Guid>

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

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -176,155 +176,6 @@ public abstract class DomainEvents_Tests<TStartupModule> : TestAppTestBase<TStar
isDistributedEventTriggered.ShouldBeTrue(); isDistributedEventTriggered.ShouldBeTrue();
} }
[Fact]
public async Task Should_Trigger_Domain_Events_For_Aggregate_Root_When_Navigation_Changes_Tests()
{
var entityId = Guid.NewGuid();
await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity"));
var entityUpdatedEventTriggered = false;
var personCreatedEventCount = 0;
var entityUpdatedEventTriggerCount = 0;
LocalEventBus.Subscribe<EntityCreatedEventData<Person>>(data =>
{
personCreatedEventCount++;
return Task.CompletedTask;
});
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigations>>(async data =>
{
entityUpdatedEventTriggered = !entityUpdatedEventTriggered;
await PersonRepository.InsertAsync(new Person(Guid.NewGuid(), Guid.NewGuid().ToString(), new Random().Next(1, 100)));
});
// Test with simple property
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.Name = Guid.NewGuid().ToString();
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithValueObjectAddress>>(data =>
{
throw new Exception("Should not trigger this event");
});
// 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();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.AppEntityWithValueObjectAddress = null;
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
// 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();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationChildOneToOne>>(data =>
{
throw new Exception("Should not trigger this event");
});
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToOne = null;
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
// 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();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigationChildOneToMany>>(data =>
{
throw new Exception("Should not trigger this event");
});
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToMany.Clear();
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
// 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();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
entityUpdatedEventTriggered = false;
await WithUnitOfWorkAsync(async () =>
{
var entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.ManyToMany.Clear();
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
entityUpdatedEventTriggered.ShouldBeTrue();
personCreatedEventCount.ShouldBe(++entityUpdatedEventTriggerCount);
}
private class MyCustomEventData private class MyCustomEventData
{ {
public string Value { get; set; } public string Value { get; set; }
@ -368,7 +219,7 @@ public abstract class AbpEntityChangeOptions_DomainEvents_Tests<TStartupModule>
LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigations>>(data => LocalEventBus.Subscribe<EntityUpdatedEventData<AppEntityWithNavigations>>(data =>
{ {
entityUpdatedEventTriggered = !entityUpdatedEventTriggered; entityUpdatedEventTriggered = true;
return Task.CompletedTask; return Task.CompletedTask;
}); });

Loading…
Cancel
Save