Skip to content

Commit

Permalink
Apply AbpConcepts to entities which navigation property changes.
Browse files Browse the repository at this point in the history
Resolve #19962
  • Loading branch information
maliming committed Jun 11, 2024
1 parent 82ccefe commit abf2824
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
{
if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
if (entityEntry.State == EntityState.Unchanged)
{
ApplyAbpConceptsForModifiedEntity(entityEntry, true);
}

if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
Expand Down Expand Up @@ -274,7 +279,7 @@ public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
}
}

private void PublishEntityEvents(EntityEventReport changeReport)
protected virtual void PublishEntityEvents(EntityEventReport changeReport)
{
foreach (var localEvent in changeReport.DomainEvents)
{
Expand Down Expand Up @@ -388,7 +393,6 @@ protected virtual void PublishEventsForTrackedEntity(EntityEntry entry)
break;

case EntityState.Modified:
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()))
Expand All @@ -397,6 +401,7 @@ protected virtual void PublishEventsForTrackedEntity(EntityEntry entry)
break;
}

ApplyAbpConceptsForModifiedEntity(entry);
if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity);
Expand All @@ -406,8 +411,9 @@ protected virtual void PublishEventsForTrackedEntity(EntityEntry entry)
EntityChangeEventHelper.PublishEntityUpdatedEvent(entry.Entity);
}
}
else if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges && AbpEfCoreNavigationHelper.IsEntityEntryModified(entry))
else if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges && AbpEfCoreNavigationHelper.IsNavigationEntryModified(entry))
{
ApplyAbpConceptsForModifiedEntity(entry, true);
if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity);
Expand All @@ -428,7 +434,8 @@ protected virtual void PublishEventsForTrackedEntity(EntityEntry entry)

protected virtual void HandlePropertiesBeforeSave()
{
foreach (var entry in ChangeTracker.Entries().ToList())
var entries = ChangeTracker.Entries().ToList();
foreach (var entry in entries)
{
HandleExtraPropertiesOnSave(entry);

Expand All @@ -437,6 +444,14 @@ protected virtual void HandlePropertiesBeforeSave()
UpdateConcurrencyStamp(entry);
}
}

if (EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
foreach (var entry in AbpEfCoreNavigationHelper.GetChangedEntityEntries().Where(x => x.State == EntityState.Unchanged))
{
UpdateConcurrencyStamp(entry);
}
}
}

protected virtual EntityEventReport CreateEventReport()
Expand Down Expand Up @@ -565,9 +580,10 @@ protected virtual void ApplyAbpConceptsForAddedEntity(EntityEntry entry)
SetCreationAuditProperties(entry);
}

protected virtual void ApplyAbpConceptsForModifiedEntity(EntityEntry entry)
protected virtual void ApplyAbpConceptsForModifiedEntity(EntityEntry entry, bool forceApply = false)
{
if (entry.State == EntityState.Modified && entry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd)))
if (forceApply ||
entry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd)))
{
IncrementEntityVersionProperty(entry);
SetModificationAuditProperties(entry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public virtual bool IsEntityEntryModified(EntityEntry entityEntry)
return EntityEntries.TryGetValue(entryId, out var abpEntityEntry) && abpEntityEntry.IsModified;
}

public virtual bool IsNavigationEntryModified(EntityEntry entityEntry, int navigationEntryIndex)
public virtual bool IsNavigationEntryModified(EntityEntry entityEntry, int? navigationEntryIndex = null)
{
var entryId = GetEntityEntryIdentity(entityEntry);
if (entryId == null)
Expand All @@ -180,7 +180,12 @@ public virtual bool IsNavigationEntryModified(EntityEntry entityEntry, int navi
return false;
}

var navigationEntryProperty = abpEntityEntry.NavigationEntries.ElementAtOrDefault(navigationEntryIndex);
if (navigationEntryIndex == null)
{
return abpEntityEntry.NavigationEntries.Any(x => x.IsModified);
}

var navigationEntryProperty = abpEntityEntry.NavigationEntries.ElementAtOrDefault(navigationEntryIndex.Value);
return navigationEntryProperty != null && navigationEntryProperty.IsModified;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Volo.Abp.TestApp.Testing;

namespace Volo.Abp.EntityFrameworkCore.Domain;

public class EntityChange_Tests : EntityChange_Tests<AbpEntityFrameworkCoreTestModule>
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Volo.Abp.TestApp.Testing;
using Xunit;

namespace Volo.Abp.MongoDB.Domain;

[Collection(MongoTestCollection.Name)]
public class EntityChange_Tests : EntityChange_Tests<AbpMongoDbTestModule>
{

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.Domain.Values;

namespace Volo.Abp.TestApp.Domain;

public class AppEntityWithNavigations : AggregateRoot<Guid>
public class AppEntityWithNavigations : FullAuditedAggregateRoot<Guid>
{
protected AppEntityWithNavigations()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Modularity;
using Volo.Abp.TestApp.Domain;
using Xunit;

namespace Volo.Abp.TestApp.Testing;

public abstract class EntityChange_Tests<TStartupModule> : TestAppTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
protected readonly IRepository<AppEntityWithNavigations, Guid> AppEntityWithNavigationsRepository;

protected EntityChange_Tests()
{
AppEntityWithNavigationsRepository = GetRequiredService<IRepository<AppEntityWithNavigations, Guid>>();
}

[Fact]
public async Task Should_Update_AbpConcepts_Properties_When_Entity_Or_Its_Navigation_Property_Changed()
{
var entityId = Guid.NewGuid();
var entity = await AppEntityWithNavigationsRepository.InsertAsync(new AppEntityWithNavigations(entityId, "TestEntity"));
var concurrencyStamp = entity.ConcurrencyStamp;
var lastModificationTime = entity.LastModificationTime;

// Test with simple property
await WithUnitOfWorkAsync(async () =>
{
entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.Name = Guid.NewGuid().ToString();
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
concurrencyStamp.ShouldNotBe(entity.ConcurrencyStamp);
lastModificationTime.ShouldNotBe(entity.LastModificationTime);
concurrencyStamp = entity.ConcurrencyStamp;
lastModificationTime = entity.LastModificationTime;

// Test with value object
await WithUnitOfWorkAsync(async () =>
{
entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.AppEntityWithValueObjectAddress = new AppEntityWithValueObjectAddress("Turkey");
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
concurrencyStamp.ShouldNotBe(entity.ConcurrencyStamp);
lastModificationTime.ShouldNotBe(entity.LastModificationTime);
concurrencyStamp = entity.ConcurrencyStamp;
lastModificationTime = entity.LastModificationTime;

// Test with one to one
await WithUnitOfWorkAsync(async () =>
{
entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToOne = new AppEntityWithNavigationChildOneToOne
{
ChildName = "ChildName"
};
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
concurrencyStamp.ShouldNotBe(entity.ConcurrencyStamp);
lastModificationTime.ShouldNotBe(entity.LastModificationTime);
concurrencyStamp = entity.ConcurrencyStamp;
lastModificationTime = entity.LastModificationTime;

// Test with one to many
await WithUnitOfWorkAsync(async () =>
{
entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.OneToMany = new List<AppEntityWithNavigationChildOneToMany>()
{
new AppEntityWithNavigationChildOneToMany
{
AppEntityWithNavigationId = entity.Id,
ChildName = "ChildName1"
}
};
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
concurrencyStamp.ShouldNotBe(entity.ConcurrencyStamp);
lastModificationTime.ShouldNotBe(entity.LastModificationTime);
concurrencyStamp = entity.ConcurrencyStamp;
lastModificationTime = entity.LastModificationTime;

// Test with many to many
await WithUnitOfWorkAsync(async () =>
{
entity = await AppEntityWithNavigationsRepository.GetAsync(entityId);
entity.ManyToMany = new List<AppEntityWithNavigationChildManyToMany>()
{
new AppEntityWithNavigationChildManyToMany
{
ChildName = "ChildName1"
}
};
await AppEntityWithNavigationsRepository.UpdateAsync(entity);
});
concurrencyStamp.ShouldNotBe(entity.ConcurrencyStamp);
lastModificationTime.ShouldNotBe(entity.LastModificationTime);

}
}

0 comments on commit abf2824

Please sign in to comment.