Skip to content

Commit

Permalink
Publish EntityUpdatedEvent when navigation changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
maliming committed Mar 15, 2024
1 parent 9b8b2c0 commit e44d7f6
Show file tree
Hide file tree
Showing 17 changed files with 407 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Volo.Abp.Domain.Entities.Events;

public class AbpEntityChangeOptions
{
/// <summary>
/// Default: true.
/// Publish the EntityUpdatedEvent when any navigation property changes.
/// </summary>
public bool PublishEntityUpdatedEventWhenNavigationChanges { get; set; } = true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,

public IEntityChangeEventHelper EntityChangeEventHelper => LazyServiceProvider.LazyGetService<IEntityChangeEventHelper>(NullEntityChangeEventHelper.Instance);

public IOptions<AbpEntityChangeOptions> EntityChangeOptions => LazyServiceProvider.LazyGetRequiredService<IOptions<AbpEntityChangeOptions>>();

public IAuditPropertySetter AuditPropertySetter => LazyServiceProvider.LazyGetRequiredService<IAuditPropertySetter>();

public IEntityHistoryHelper EntityHistoryHelper => LazyServiceProvider.LazyGetService<IEntityHistoryHelper>(NullEntityHistoryHelper.Instance);
Expand Down Expand Up @@ -336,34 +338,59 @@ protected virtual void FillExtraPropertiesForTrackedEntities(EntityTrackedEventA
}
}

private void PublishEventsForTrackedEntity(EntityEntry entry)
protected virtual void PublishEventsForTrackedEntity(EntityEntry entry)
{
switch (entry.State)
switch (entry.State)
{
case EntityState.Added:
ApplyAbpConceptsForAddedEntity(entry);
EntityChangeEventHelper.PublishEntityCreatedEvent(entry.Entity);
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.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entry.Entity);
}
}

break;
case EntityState.Deleted:
ApplyAbpConceptsForDeletedEntity(entry);
EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity);
break;
}

foreach (var entityEntry in new[] { entry }.Where(HasEntityEntryChanged).Concat(ChangeTracker.Entries().Where(HasEntityEntryChanged)).DistinctBy(x => x.Entity))
{
ApplyAbpConceptsForModifiedEntity(entry);

if (entityEntry.Entity is ISoftDelete && entityEntry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletedEvent(entityEntry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatedEvent(entityEntry.Entity);
}
}
}

protected virtual bool HasEntityEntryChanged(EntityEntry entry)
{
if (entry.State != EntityState.Modified && entry.State != EntityState.Unchanged)
{
return false;
}

var changed = entry.State == EntityState.Modified && entry.Properties.Any(x => x.IsModified && (x.Metadata.ValueGenerated == ValueGenerated.Never || x.Metadata.ValueGenerated == ValueGenerated.OnAdd));
if (changed)
{
return true;
}

if (!changed &&
EntityChangeOptions.Value.PublishEntityUpdatedEventWhenNavigationChanges)
{
if (entry.Navigations.Any(navigation => navigation.IsModified || (navigation is ReferenceEntry && navigation.As<ReferenceEntry>().TargetEntry?.State == EntityState.Modified)))
{
changed = true;
}
}

return changed;
}

protected virtual void HandlePropertiesBeforeSave()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ public interface ITestCounter
int Increment(string name);

int GetValue(string name);

void ResetCount(string name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,12 @@ public int GetValue(string name)
return _values.GetOrDefault(name);
}
}

public void ResetCount(string name)
{
lock (_values)
{
_values[name] = 0;
}
}
}
1 change: 1 addition & 0 deletions framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ public virtual void OnCompleted(Func<Task> handler)
}
else
{
eventRecord.SetOrder(eventRecords[foundIndex].EventOrder);
eventRecords[foundIndex] = eventRecord;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class UnitOfWorkEventRecord

public Type EventType { get; }

public long EventOrder { get; }
public long EventOrder { get; protected set; }

public bool UseOutbox { get; }

Expand All @@ -29,4 +29,9 @@ public class UnitOfWorkEventRecord
EventOrder = eventOrder;
UseOutbox = useOutbox;
}

public void SetOrder(long order)
{
EventOrder = order;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public override void ConfigureServices(ServiceConfigurationContext context)
{
opt.DefaultWithDetailsFunc = q => q.Include(p => p.Books);
});
options.Entity<AppEntityWithNavigations>(opt =>
{
opt.DefaultWithDetailsFunc = q => q.Include(p => p.OneToOne).Include(p => p.OneToMany).Include(p => p.ManyToMany);
});
});

context.Services.AddAbpDbContext<HostTestAppDbContext>(options =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ namespace Volo.Abp.EntityFrameworkCore.DomainEvents;
public class DomainEvents_Tests : DomainEvents_Tests<AbpEntityFrameworkCoreTestModule>
{
}

public class AbpEntityChangeOptions_DomainEvents_Tests : AbpEntityChangeOptions_DomainEvents_Tests<AbpEntityFrameworkCoreTestModule>
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class TestMigrationsDbContext : AbpDbContext<TestMigrationsDbContext>

public DbSet<Category> Categories { get; set; }

public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; }

public TestMigrationsDbContext(DbContextOptions<TestMigrationsDbContext> options)
: base(options)
{
Expand Down Expand Up @@ -64,5 +66,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
b.HasAbpQueryFilter(e => e.Name.StartsWith("abp"));
});

modelBuilder.Entity<AppEntityWithNavigations>(b =>
{
b.ConfigureByConvention();
b.OwnsOne(v => v.AppEntityWithValueObjectAddress);
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.ManyToMany).WithMany();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext,

public DbSet<Category> Categories { get; set; }

public DbSet<AppEntityWithNavigations> AppEntityWithNavigations { get; set; }

public TestAppDbContext(DbContextOptions<TestAppDbContext> options)
: base(options)
{
Expand Down Expand Up @@ -92,6 +94,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
b.HasAbpQueryFilter(e => e.Name.StartsWith("abp"));
});

modelBuilder.Entity<AppEntityWithNavigations>(b =>
{
b.ConfigureByConvention();
b.OwnsOne(v => v.AppEntityWithValueObjectAddress);
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.ManyToMany).WithMany();
});

modelBuilder.TryConfigureObjectExtensions<TestAppDbContext>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public class TestAppMemoryDbContext : MemoryDbContext
private static readonly Type[] EntityTypeList = {
typeof(Person),
typeof(EntityWithIntPk),
typeof(Product)
typeof(Product),
typeof(AppEntityWithNavigations)
};

public override IReadOnlyList<Type> GetEntityTypes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ protected override void AfterAddApplication(IServiceCollection services)
// We must reconfigure it in the new unit test.
foreach (var registeredClassMap in BsonClassMap.GetRegisteredClassMaps())
{
var frozen = registeredClassMap.GetType().BaseType?.GetField("_frozen", BindingFlags.NonPublic | BindingFlags.Instance);
frozen?.SetValue(registeredClassMap, false);

foreach (var declaredMemberMap in registeredClassMap.DeclaredMemberMaps)
{
var serializer = declaredMemberMap.GetSerializer();
Expand All @@ -46,8 +43,6 @@ protected override void AfterAddApplication(IServiceCollection services)
}
}
}

frozen?.SetValue(registeredClassMap, true);
}
}
}
Expand All @@ -62,6 +57,7 @@ protected override void AfterAddApplication(IServiceCollection services)
}
}

[Collection(MongoTestCollection.Name)]
public class DateTimeKindTests_Local : MongoDB_DateTimeKind_Tests
{
protected override void AfterAddApplication(IServiceCollection services)
Expand All @@ -72,6 +68,7 @@ protected override void AfterAddApplication(IServiceCollection services)
}
}

[Collection(MongoTestCollection.Name)]
public class DateTimeKindTests_Utc : MongoDB_DateTimeKind_Tests
{
protected override void AfterAddApplication(IServiceCollection services)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ public interface ITestAppMongoDbContext : IAbpMongoDbContext
IMongoCollection<City> Cities { get; }

IMongoCollection<Product> Products { get; }

IMongoCollection<AppEntityWithNavigations> AppEntityWithNavigations { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ public class TestAppMongoDbContext : AbpMongoDbContext, ITestAppMongoDbContext,
public IMongoCollection<ThirdDbContextDummyEntity> DummyEntities => Collection<ThirdDbContextDummyEntity>();

public IMongoCollection<FourthDbContextDummyEntity> FourthDummyEntities => Collection<FourthDbContextDummyEntity>();

public IMongoCollection<Product> Products => Collection<Product>();

public IMongoCollection<AppEntityWithNavigations> AppEntityWithNavigations => Collection<AppEntityWithNavigations>();

protected internal override void CreateModel(IMongoModelBuilder modelBuilder)
{
base.CreateModel(modelBuilder);
Expand All @@ -36,7 +38,7 @@ protected internal override void CreateModel(IMongoModelBuilder modelBuilder)
{
b.CollectionName = "MyCities";
});

modelBuilder.Entity<Person>(b =>
{
b.CreateCollectionOptions.Collation = new Collation(locale:"en_US", strength: CollationStrength.Secondary);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Values;

namespace Volo.Abp.TestApp.Domain;

public class AppEntityWithNavigations : AggregateRoot<Guid>
{
protected AppEntityWithNavigations()
{

}

public AppEntityWithNavigations(Guid id, string name)
: base(id)
{
Name = name;
FullName = name;
}

public string Name { get; set; }

public string FullName { get; set; }

public AppEntityWithValueObjectAddress AppEntityWithValueObjectAddress { get; set; }

public virtual AppEntityWithNavigationChildOneToOne OneToOne { get; set; }

public virtual List<AppEntityWithNavigationChildOneToMany> OneToMany { get; set; }

public virtual List<AppEntityWithNavigationChildManyToMany> ManyToMany { get; set; }
}

public class AppEntityWithValueObjectAddress : ValueObject
{
public AppEntityWithValueObjectAddress(string country)
{

Country = country;
}

public string Country { get; set; }

protected override IEnumerable<object> GetAtomicValues()
{
yield return Country;
}
}


public class AppEntityWithNavigationChildOneToOne : Entity<Guid>
{
public string ChildName { get; set; }
}

public class AppEntityWithNavigationChildOneToMany : Entity<Guid>
{
public Guid AppEntityWithNavigationId { get; set; }

public string ChildName { get; set; }
}

public class AppEntityWithNavigationChildManyToMany : Entity<Guid>
{
public string ChildName { get; set; }
}

0 comments on commit e44d7f6

Please sign in to comment.