Browse Source

Events should be published and handled in the right order.

pull/9909/head
Halil İbrahim Kalkan 5 years ago
parent
commit
258cf783f7
  1. 25
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/BasicAggregateRoot.cs
  2. 15
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/DomainEventRecord.cs
  3. 5
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/DomainEventEntry.cs
  4. 91
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs
  5. 32
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeReport.cs
  6. 22
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityEventReport.cs
  7. 16
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/IEntityChangeEventHelper.cs
  8. 26
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/NullEntityChangeEventHelper.cs
  9. 4
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/IGeneratesDomainEvents.cs
  10. 176
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  11. 2
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs
  12. 47
      framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
  13. 53
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
  14. 13
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/AmbientUnitOfWork.cs
  15. 14
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/EventOrderGenerator.cs
  16. 2
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IAmbientUnitOfWork.cs
  17. 4
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs
  18. 6
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkEventRecord.cs
  19. 15
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkManager.cs
  20. 63
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs
  21. 14
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs

25
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/BasicAggregateRoot.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Volo.Abp.Uow;
namespace Volo.Abp.Domain.Entities namespace Volo.Abp.Domain.Entities
{ {
@ -9,15 +10,15 @@ namespace Volo.Abp.Domain.Entities
IAggregateRoot, IAggregateRoot,
IGeneratesDomainEvents IGeneratesDomainEvents
{ {
private readonly ICollection<object> _distributedEvents = new Collection<object>(); private readonly ICollection<DomainEventRecord> _distributedEvents = new Collection<DomainEventRecord>();
private readonly ICollection<object> _localEvents = new Collection<object>(); private readonly ICollection<DomainEventRecord> _localEvents = new Collection<DomainEventRecord>();
public virtual IEnumerable<object> GetLocalEvents() public virtual IEnumerable<DomainEventRecord> GetLocalEvents()
{ {
return _localEvents; return _localEvents;
} }
public virtual IEnumerable<object> GetDistributedEvents() public virtual IEnumerable<DomainEventRecord> GetDistributedEvents()
{ {
return _distributedEvents; return _distributedEvents;
} }
@ -34,12 +35,12 @@ namespace Volo.Abp.Domain.Entities
protected virtual void AddLocalEvent(object eventData) protected virtual void AddLocalEvent(object eventData)
{ {
_localEvents.Add(eventData); _localEvents.Add(new DomainEventRecord(eventData, EventOrderGenerator.GetNext()));
} }
protected virtual void AddDistributedEvent(object eventData) protected virtual void AddDistributedEvent(object eventData)
{ {
_distributedEvents.Add(eventData); _distributedEvents.Add(new DomainEventRecord(eventData, EventOrderGenerator.GetNext()));
} }
} }
@ -48,8 +49,8 @@ namespace Volo.Abp.Domain.Entities
IAggregateRoot<TKey>, IAggregateRoot<TKey>,
IGeneratesDomainEvents IGeneratesDomainEvents
{ {
private readonly ICollection<object> _distributedEvents = new Collection<object>(); private readonly ICollection<DomainEventRecord> _distributedEvents = new Collection<DomainEventRecord>();
private readonly ICollection<object> _localEvents = new Collection<object>(); private readonly ICollection<DomainEventRecord> _localEvents = new Collection<DomainEventRecord>();
protected BasicAggregateRoot() protected BasicAggregateRoot()
{ {
@ -62,12 +63,12 @@ namespace Volo.Abp.Domain.Entities
} }
public virtual IEnumerable<object> GetLocalEvents() public virtual IEnumerable<DomainEventRecord> GetLocalEvents()
{ {
return _localEvents; return _localEvents;
} }
public virtual IEnumerable<object> GetDistributedEvents() public virtual IEnumerable<DomainEventRecord> GetDistributedEvents()
{ {
return _distributedEvents; return _distributedEvents;
} }
@ -84,12 +85,12 @@ namespace Volo.Abp.Domain.Entities
protected virtual void AddLocalEvent(object eventData) protected virtual void AddLocalEvent(object eventData)
{ {
_localEvents.Add(eventData); _localEvents.Add(new DomainEventRecord(eventData, EventOrderGenerator.GetNext()));
} }
protected virtual void AddDistributedEvent(object eventData) protected virtual void AddDistributedEvent(object eventData)
{ {
_distributedEvents.Add(eventData); _distributedEvents.Add(new DomainEventRecord(eventData, EventOrderGenerator.GetNext()));
} }
} }
} }

15
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/DomainEventRecord.cs

@ -0,0 +1,15 @@
namespace Volo.Abp.Domain.Entities
{
public class DomainEventRecord
{
public object EventData { get; }
public long EventOrder { get; }
public DomainEventRecord(object eventData, long eventOrder)
{
EventData = eventData;
EventOrder = eventOrder;
}
}
}

5
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/DomainEventEntry.cs

@ -8,11 +8,14 @@ namespace Volo.Abp.Domain.Entities.Events
public object SourceEntity { get; } public object SourceEntity { get; }
public object EventData { get; } public object EventData { get; }
public long EventOrder { get; }
public DomainEventEntry(object sourceEntity, object eventData) public DomainEventEntry(object sourceEntity, object eventData, long eventOrder)
{ {
SourceEntity = sourceEntity; SourceEntity = sourceEntity;
EventData = eventData; EventData = eventData;
EventOrder = eventOrder;
} }
} }
} }

91
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs

@ -1,10 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.DynamicProxy; using Volo.Abp.DynamicProxy;
@ -44,16 +42,9 @@ namespace Volo.Abp.Domain.Entities.Events
Logger = NullLogger<EntityChangeEventHelper>.Instance; Logger = NullLogger<EntityChangeEventHelper>.Instance;
} }
public async Task TriggerEventsAsync(EntityChangeReport changeReport) public virtual void PublishEntityCreatingEvent(object entity)
{ {
await TriggerEntityChangeEvents(changeReport.ChangedEntities); TriggerEventWithEntity(
await TriggerLocalEvents(changeReport.DomainEvents);
await TriggerDistributedEvents(changeReport.DistributedEvents);
}
public virtual async Task TriggerEntityCreatingEventAsync(object entity)
{
await TriggerEventWithEntity(
LocalEventBus, LocalEventBus,
typeof(EntityCreatingEventData<>), typeof(EntityCreatingEventData<>),
entity, entity,
@ -61,9 +52,9 @@ namespace Volo.Abp.Domain.Entities.Events
); );
} }
public virtual async Task TriggerEntityCreatedEventAsync(object entity) public virtual void PublishEntityCreatedEvent(object entity)
{ {
await TriggerEventWithEntity( TriggerEventWithEntity(
LocalEventBus, LocalEventBus,
typeof(EntityCreatedEventData<>), typeof(EntityCreatedEventData<>),
entity, entity,
@ -75,7 +66,7 @@ namespace Volo.Abp.Domain.Entities.Events
var eto = EntityToEtoMapper.Map(entity); var eto = EntityToEtoMapper.Map(entity);
if (eto != null) if (eto != null)
{ {
await TriggerEventWithEntity( TriggerEventWithEntity(
DistributedEventBus, DistributedEventBus,
typeof(EntityCreatedEto<>), typeof(EntityCreatedEto<>),
eto, eto,
@ -96,9 +87,9 @@ namespace Volo.Abp.Domain.Entities.Events
); );
} }
public virtual async Task TriggerEntityUpdatingEventAsync(object entity) public virtual void PublishEntityUpdatingEvent(object entity)
{ {
await TriggerEventWithEntity( TriggerEventWithEntity(
LocalEventBus, LocalEventBus,
typeof(EntityUpdatingEventData<>), typeof(EntityUpdatingEventData<>),
entity, entity,
@ -106,9 +97,9 @@ namespace Volo.Abp.Domain.Entities.Events
); );
} }
public virtual async Task TriggerEntityUpdatedEventAsync(object entity) public virtual void PublishEntityUpdatedEvent(object entity)
{ {
await TriggerEventWithEntity( TriggerEventWithEntity(
LocalEventBus, LocalEventBus,
typeof(EntityUpdatedEventData<>), typeof(EntityUpdatedEventData<>),
entity, entity,
@ -120,7 +111,7 @@ namespace Volo.Abp.Domain.Entities.Events
var eto = EntityToEtoMapper.Map(entity); var eto = EntityToEtoMapper.Map(entity);
if (eto != null) if (eto != null)
{ {
await TriggerEventWithEntity( TriggerEventWithEntity(
DistributedEventBus, DistributedEventBus,
typeof(EntityUpdatedEto<>), typeof(EntityUpdatedEto<>),
eto, eto,
@ -130,9 +121,9 @@ namespace Volo.Abp.Domain.Entities.Events
} }
} }
public virtual async Task TriggerEntityDeletingEventAsync(object entity) public virtual void PublishEntityDeletingEvent(object entity)
{ {
await TriggerEventWithEntity( TriggerEventWithEntity(
LocalEventBus, LocalEventBus,
typeof(EntityDeletingEventData<>), typeof(EntityDeletingEventData<>),
entity, entity,
@ -140,9 +131,9 @@ namespace Volo.Abp.Domain.Entities.Events
); );
} }
public virtual async Task TriggerEntityDeletedEventAsync(object entity) public virtual void PublishEntityDeletedEvent(object entity)
{ {
await TriggerEventWithEntity( TriggerEventWithEntity(
LocalEventBus, LocalEventBus,
typeof(EntityDeletedEventData<>), typeof(EntityDeletedEventData<>),
entity, entity,
@ -154,7 +145,7 @@ namespace Volo.Abp.Domain.Entities.Events
var eto = EntityToEtoMapper.Map(entity); var eto = EntityToEtoMapper.Map(entity);
if (eto != null) if (eto != null)
{ {
await TriggerEventWithEntity( TriggerEventWithEntity(
DistributedEventBus, DistributedEventBus,
typeof(EntityDeletedEto<>), typeof(EntityDeletedEto<>),
eto, eto,
@ -164,48 +155,7 @@ namespace Volo.Abp.Domain.Entities.Events
} }
} }
protected virtual async Task TriggerEntityChangeEvents(List<EntityChangeEntry> changedEntities) protected virtual void TriggerEventWithEntity(
{
foreach (var changedEntity in changedEntities)
{
switch (changedEntity.ChangeType)
{
case EntityChangeType.Created:
await TriggerEntityCreatingEventAsync(changedEntity.Entity);
await TriggerEntityCreatedEventAsync(changedEntity.Entity);
break;
case EntityChangeType.Updated:
await TriggerEntityUpdatingEventAsync(changedEntity.Entity);
await TriggerEntityUpdatedEventAsync(changedEntity.Entity);
break;
case EntityChangeType.Deleted:
await TriggerEntityDeletingEventAsync(changedEntity.Entity);
await TriggerEntityDeletedEventAsync(changedEntity.Entity);
break;
default:
throw new AbpException("Unknown EntityChangeType: " + changedEntity.ChangeType);
}
}
}
protected virtual async Task TriggerLocalEvents(List<DomainEventEntry> localEvents)
{
foreach (var localEvent in localEvents)
{
await LocalEventBus.PublishAsync(localEvent.EventData.GetType(), localEvent.EventData);
}
}
protected virtual async Task TriggerDistributedEvents(List<DomainEventEntry> distributedEvents)
{
foreach (var distributedEvent in distributedEvents)
{
await DistributedEventBus.PublishAsync(distributedEvent.EventData.GetType(),
distributedEvent.EventData);
}
}
protected virtual async Task TriggerEventWithEntity(
IEventBus eventPublisher, IEventBus eventPublisher,
Type genericEventType, Type genericEventType,
object entityOrEto, object entityOrEto,
@ -218,16 +168,11 @@ namespace Volo.Abp.Domain.Entities.Events
if (currentUow == null) if (currentUow == null)
{ {
await eventPublisher.PublishAsync( Logger.LogWarning("UnitOfWorkManager.Current is null! Can not publish the event.");
eventType,
eventData,
onUnitOfWorkComplete: false
);
return; return;
} }
var eventRecord = new UnitOfWorkEventRecord(eventType, eventData) var eventRecord = new UnitOfWorkEventRecord(eventType, eventData, EventOrderGenerator.GetNext())
{ {
Properties = Properties =
{ {

32
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeReport.cs

@ -1,32 +0,0 @@
using System.Collections.Generic;
namespace Volo.Abp.Domain.Entities.Events
{
public class EntityChangeReport
{
public List<EntityChangeEntry> ChangedEntities { get; }
public List<DomainEventEntry> DomainEvents { get; }
public List<DomainEventEntry> DistributedEvents { get; }
public EntityChangeReport()
{
ChangedEntities = new List<EntityChangeEntry>();
DomainEvents = new List<DomainEventEntry>();
DistributedEvents = new List<DomainEventEntry>();
}
public bool IsEmpty()
{
return ChangedEntities.Count <= 0 &&
DomainEvents.Count <= 0 &&
DistributedEvents.Count <= 0;
}
public override string ToString()
{
return $"[EntityChangeReport] ChangedEntities: {ChangedEntities.Count}, DomainEvents: {DomainEvents.Count}, DistributedEvents: {DistributedEvents.Count}";
}
}
}

22
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityEventReport.cs

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace Volo.Abp.Domain.Entities.Events
{
public class EntityEventReport
{
public List<DomainEventEntry> DomainEvents { get; }
public List<DomainEventEntry> DistributedEvents { get; }
public EntityEventReport()
{
DomainEvents = new List<DomainEventEntry>();
DistributedEvents = new List<DomainEventEntry>();
}
public override string ToString()
{
return $"[{nameof(EntityEventReport)}] DomainEvents: {DomainEvents.Count}, DistributedEvents: {DistributedEvents.Count}";
}
}
}

16
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/IEntityChangeEventHelper.cs

@ -1,5 +1,3 @@
using System.Threading.Tasks;
namespace Volo.Abp.Domain.Entities.Events namespace Volo.Abp.Domain.Entities.Events
{ {
/// <summary> /// <summary>
@ -7,15 +5,13 @@ namespace Volo.Abp.Domain.Entities.Events
/// </summary> /// </summary>
public interface IEntityChangeEventHelper public interface IEntityChangeEventHelper
{ {
Task TriggerEventsAsync(EntityChangeReport changeReport); void PublishEntityCreatingEvent(object entity);
void PublishEntityCreatedEvent(object entity);
Task TriggerEntityCreatingEventAsync(object entity);
Task TriggerEntityCreatedEventAsync(object entity);
Task TriggerEntityUpdatingEventAsync(object entity); void PublishEntityUpdatingEvent(object entity);
Task TriggerEntityUpdatedEventAsync(object entity); void PublishEntityUpdatedEvent(object entity);
Task TriggerEntityDeletingEventAsync(object entity); void PublishEntityDeletingEvent(object entity);
Task TriggerEntityDeletedEventAsync(object entity); void PublishEntityDeletedEvent(object entity);
} }
} }

26
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/NullEntityChangeEventHelper.cs

@ -1,5 +1,3 @@
using System.Threading.Tasks;
namespace Volo.Abp.Domain.Entities.Events namespace Volo.Abp.Domain.Entities.Events
{ {
/// <summary> /// <summary>
@ -14,42 +12,30 @@ namespace Volo.Abp.Domain.Entities.Events
private NullEntityChangeEventHelper() private NullEntityChangeEventHelper()
{ {
}
public Task TriggerEntityCreatingEventAsync(object entity)
{
return Task.CompletedTask;
} }
public Task TriggerEntityCreatedEventAsync(object entity) public void PublishEntityCreatingEvent(object entity)
{ {
return Task.CompletedTask;
} }
public Task TriggerEntityUpdatingEventAsync(object entity) public void PublishEntityCreatedEvent(object entity)
{ {
return Task.CompletedTask;
} }
public Task TriggerEntityUpdatedEventAsync(object entity) public void PublishEntityUpdatingEvent(object entity)
{ {
return Task.CompletedTask;
} }
public Task TriggerEntityDeletingEventAsync(object entity) public void PublishEntityUpdatedEvent(object entity)
{ {
return Task.CompletedTask;
} }
public Task TriggerEntityDeletedEventAsync(object entity) public void PublishEntityDeletingEvent(object entity)
{ {
return Task.CompletedTask;
} }
public Task TriggerEventsAsync(EntityChangeReport changeReport) public void PublishEntityDeletedEvent(object entity)
{ {
return Task.CompletedTask;
} }
} }
} }

4
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/IGeneratesDomainEvents.cs

@ -6,9 +6,9 @@ namespace Volo.Abp.Domain.Entities
public interface IGeneratesDomainEvents public interface IGeneratesDomainEvents
{ {
IEnumerable<object> GetLocalEvents(); IEnumerable<DomainEventRecord> GetLocalEvents();
IEnumerable<object> GetDistributedEvents(); IEnumerable<DomainEventRecord> GetDistributedEvents();
void ClearLocalEvents(); void ClearLocalEvents();

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

@ -22,6 +22,8 @@ using Volo.Abp.Domain.Repositories;
using Volo.Abp.EntityFrameworkCore.EntityHistory; using Volo.Abp.EntityFrameworkCore.EntityHistory;
using Volo.Abp.EntityFrameworkCore.Modeling; using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.Abp.EntityFrameworkCore.ValueConverters; using Volo.Abp.EntityFrameworkCore.ValueConverters;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectExtending; using Volo.Abp.ObjectExtending;
@ -59,6 +61,10 @@ namespace Volo.Abp.EntityFrameworkCore
public IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService<IUnitOfWorkManager>(); public IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService<IUnitOfWorkManager>();
public IClock Clock => LazyServiceProvider.LazyGetRequiredService<IClock>(); public IClock Clock => LazyServiceProvider.LazyGetRequiredService<IClock>();
public IDistributedEventBus DistributedEventBus => LazyServiceProvider.LazyGetRequiredService<IDistributedEventBus>();
public ILocalEventBus LocalEventBus => LazyServiceProvider.LazyGetRequiredService<ILocalEventBus>();
public ILogger<AbpDbContext<TDbContext>> Logger => LazyServiceProvider.LazyGetService<ILogger<AbpDbContext<TDbContext>>>(NullLogger<AbpDbContext<TDbContext>>.Instance); public ILogger<AbpDbContext<TDbContext>> Logger => LazyServiceProvider.LazyGetService<ILogger<AbpDbContext<TDbContext>>>(NullLogger<AbpDbContext<TDbContext>>.Instance);
@ -150,20 +156,21 @@ namespace Volo.Abp.EntityFrameworkCore
try try
{ {
var auditLog = AuditingManager?.Current?.Log; var auditLog = AuditingManager?.Current?.Log;
List<EntityChangeInfo> entityChangeList = null; List<EntityChangeInfo> entityChangeList = null;
if (auditLog != null) if (auditLog != null)
{ {
entityChangeList = EntityHistoryHelper.CreateChangeList(ChangeTracker.Entries().ToList()); entityChangeList = EntityHistoryHelper.CreateChangeList(ChangeTracker.Entries().ToList());
} }
var changeReport = ApplyAbpConcepts(); ApplyAbpConcepts();
var eventReport = CreateEventReport();
var result = await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); var result = await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
await EntityChangeEventHelper.TriggerEventsAsync(changeReport); PublishEntityEvents(eventReport);
if (auditLog != null) if (entityChangeList != null)
{ {
EntityHistoryHelper.UpdateChangeList(entityChangeList); EntityHistoryHelper.UpdateChangeList(entityChangeList);
auditLog.EntityChanges.AddRange(entityChangeList); auditLog.EntityChanges.AddRange(entityChangeList);
@ -182,6 +189,23 @@ namespace Volo.Abp.EntityFrameworkCore
} }
} }
private void PublishEntityEvents(EntityEventReport changeReport)
{
foreach (var localEvent in changeReport.DomainEvents)
{
UnitOfWorkManager.Current?.AddOrReplaceLocalEvent(
new UnitOfWorkEventRecord(localEvent.EventData.GetType(), localEvent.EventData, localEvent.EventOrder)
);
}
foreach (var distributedEvent in changeReport.DistributedEvents)
{
UnitOfWorkManager.Current?.AddOrReplaceDistributedEvent(
new UnitOfWorkEventRecord(distributedEvent.EventData.GetType(), distributedEvent.EventData, distributedEvent.EventOrder)
);
}
}
/// <summary> /// <summary>
/// This method will call the DbContext <see cref="SaveChangesAsync(bool, CancellationToken)"/> method directly of EF Core, which doesn't apply concepts of abp. /// This method will call the DbContext <see cref="SaveChangesAsync(bool, CancellationToken)"/> method directly of EF Core, which doesn't apply concepts of abp.
/// </summary> /// </summary>
@ -202,11 +226,18 @@ namespace Volo.Abp.EntityFrameworkCore
ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges; ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
ChangeTracker.Tracked += ChangeTracker_Tracked; ChangeTracker.Tracked += ChangeTracker_Tracked;
ChangeTracker.StateChanged += ChangeTracker_StateChanged;
} }
protected virtual void ChangeTracker_Tracked(object sender, EntityTrackedEventArgs e) protected virtual void ChangeTracker_Tracked(object sender, EntityTrackedEventArgs e)
{ {
FillExtraPropertiesForTrackedEntities(e); FillExtraPropertiesForTrackedEntities(e);
PublishEventsForTrackedEntity(e.Entry);
}
protected virtual void ChangeTracker_StateChanged(object sender, EntityStateChangedEventArgs e)
{
PublishEventsForTrackedEntity(e.Entry);
} }
protected virtual void FillExtraPropertiesForTrackedEntities(EntityTrackedEventArgs e) protected virtual void FillExtraPropertiesForTrackedEntities(EntityTrackedEventArgs e)
@ -254,37 +285,107 @@ namespace Volo.Abp.EntityFrameworkCore
} }
} }
} }
protected virtual EntityChangeReport ApplyAbpConcepts() private void PublishEventsForTrackedEntity(EntityEntry entry)
{ {
var changeReport = new EntityChangeReport(); switch (entry.State)
{
case EntityState.Added:
EntityChangeEventHelper.PublishEntityCreatingEvent(entry.Entity);
EntityChangeEventHelper.PublishEntityCreatedEvent(entry.Entity);
break;
case EntityState.Modified:
if (entry.Properties.Any(x => x.IsModified && x.Metadata.ValueGenerated == ValueGenerated.Never))
{
if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
{
EntityChangeEventHelper.PublishEntityDeletingEvent(entry.Entity);
EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity);
}
else
{
EntityChangeEventHelper.PublishEntityUpdatingEvent(entry.Entity);
EntityChangeEventHelper.PublishEntityUpdatedEvent(entry.Entity);
}
}
break;
case EntityState.Deleted:
EntityChangeEventHelper.PublishEntityDeletingEvent(entry.Entity);
EntityChangeEventHelper.PublishEntityDeletedEvent(entry.Entity);
break;
}
}
protected virtual void ApplyAbpConcepts()
{
foreach (var entry in ChangeTracker.Entries().ToList()) foreach (var entry in ChangeTracker.Entries().ToList())
{ {
ApplyAbpConcepts(entry, changeReport); ApplyAbpConcepts(entry);
} }
return changeReport;
} }
protected virtual EntityEventReport CreateEventReport()
{
var eventReport = new EntityEventReport();
foreach (var entry in ChangeTracker.Entries().ToList())
{
var generatesDomainEventsEntity = entry.Entity as IGeneratesDomainEvents;
if (generatesDomainEventsEntity == null)
{
continue;
}
protected virtual void ApplyAbpConcepts(EntityEntry entry, EntityChangeReport changeReport) var localEvents = generatesDomainEventsEntity.GetLocalEvents()?.ToArray();
if (localEvents != null && localEvents.Any())
{
eventReport.DomainEvents.AddRange(
localEvents.Select(
eventRecord => new DomainEventEntry(
entry.Entity,
eventRecord.EventData,
eventRecord.EventOrder
)
)
);
generatesDomainEventsEntity.ClearLocalEvents();
}
var distributedEvents = generatesDomainEventsEntity.GetDistributedEvents()?.ToArray();
if (distributedEvents != null && distributedEvents.Any())
{
eventReport.DistributedEvents.AddRange(
distributedEvents.Select(
eventRecord => new DomainEventEntry(
entry.Entity,
eventRecord.EventData,
eventRecord.EventOrder)
)
);
generatesDomainEventsEntity.ClearDistributedEvents();
}
}
return eventReport;
}
protected virtual void ApplyAbpConcepts(EntityEntry entry)
{ {
switch (entry.State) switch (entry.State)
{ {
case EntityState.Added: case EntityState.Added:
ApplyAbpConceptsForAddedEntity(entry, changeReport); ApplyAbpConceptsForAddedEntity(entry);
break; break;
case EntityState.Modified: case EntityState.Modified:
ApplyAbpConceptsForModifiedEntity(entry, changeReport); ApplyAbpConceptsForModifiedEntity(entry);
break; break;
case EntityState.Deleted: case EntityState.Deleted:
ApplyAbpConceptsForDeletedEntity(entry, changeReport); ApplyAbpConceptsForDeletedEntity(entry);
break; break;
} }
HandleExtraPropertiesOnSave(entry); HandleExtraPropertiesOnSave(entry);
AddDomainEvents(changeReport, entry.Entity);
} }
protected virtual void HandleExtraPropertiesOnSave(EntityEntry entry) protected virtual void HandleExtraPropertiesOnSave(EntityEntry entry)
@ -361,15 +462,14 @@ namespace Volo.Abp.EntityFrameworkCore
} }
} }
protected virtual void ApplyAbpConceptsForAddedEntity(EntityEntry entry, EntityChangeReport changeReport) protected virtual void ApplyAbpConceptsForAddedEntity(EntityEntry entry)
{ {
CheckAndSetId(entry); CheckAndSetId(entry);
SetConcurrencyStampIfNull(entry); SetConcurrencyStampIfNull(entry);
SetCreationAuditProperties(entry); SetCreationAuditProperties(entry);
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Created));
} }
protected virtual void ApplyAbpConceptsForModifiedEntity(EntityEntry entry, EntityChangeReport changeReport) protected virtual void ApplyAbpConceptsForModifiedEntity(EntityEntry entry)
{ {
if (entry.State == EntityState.Modified && entry.Properties.Any(x => x.IsModified && x.Metadata.ValueGenerated == ValueGenerated.Never)) if (entry.State == EntityState.Modified && entry.Properties.Any(x => x.IsModified && x.Metadata.ValueGenerated == ValueGenerated.Never))
{ {
@ -379,24 +479,17 @@ namespace Volo.Abp.EntityFrameworkCore
if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted) if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
{ {
SetDeletionAuditProperties(entry); SetDeletionAuditProperties(entry);
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
}
else
{
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Updated));
} }
} }
} }
protected virtual void ApplyAbpConceptsForDeletedEntity(EntityEntry entry, EntityChangeReport changeReport) protected virtual void ApplyAbpConceptsForDeletedEntity(EntityEntry entry)
{ {
if (TryCancelDeletionForSoftDelete(entry)) if (TryCancelDeletionForSoftDelete(entry))
{ {
UpdateConcurrencyStamp(entry); UpdateConcurrencyStamp(entry);
SetDeletionAuditProperties(entry); SetDeletionAuditProperties(entry);
} }
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
} }
protected virtual bool IsHardDeleted(EntityEntry entry) protected virtual bool IsHardDeleted(EntityEntry entry)
@ -410,29 +503,6 @@ namespace Volo.Abp.EntityFrameworkCore
return hardDeletedEntities.Contains(entry.Entity); return hardDeletedEntities.Contains(entry.Entity);
} }
protected virtual void AddDomainEvents(EntityChangeReport changeReport, object entityAsObj)
{
var generatesDomainEventsEntity = entityAsObj as IGeneratesDomainEvents;
if (generatesDomainEventsEntity == null)
{
return;
}
var localEvents = generatesDomainEventsEntity.GetLocalEvents()?.ToArray();
if (localEvents != null && localEvents.Any())
{
changeReport.DomainEvents.AddRange(localEvents.Select(eventData => new DomainEventEntry(entityAsObj, eventData)));
generatesDomainEventsEntity.ClearLocalEvents();
}
var distributedEvents = generatesDomainEventsEntity.GetDistributedEvents()?.ToArray();
if (distributedEvents != null && distributedEvents.Any())
{
changeReport.DistributedEvents.AddRange(distributedEvents.Select(eventData => new DomainEventEntry(entityAsObj, eventData)));
generatesDomainEventsEntity.ClearDistributedEvents();
}
}
protected virtual void UpdateConcurrencyStamp(EntityEntry entry) protected virtual void UpdateConcurrencyStamp(EntityEntry entry)
{ {
var entity = entry.Entity as IHasConcurrencyStamp; var entity = entry.Entity as IHasConcurrencyStamp;

2
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs

@ -104,7 +104,7 @@ namespace Volo.Abp.EventBus
{ {
AddToUnitOfWork( AddToUnitOfWork(
UnitOfWorkManager.Current, UnitOfWorkManager.Current,
new UnitOfWorkEventRecord(eventType, eventData) new UnitOfWorkEventRecord(eventType, eventData, EventOrderGenerator.GetNext())
); );
return; return;
} }

47
framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs

@ -12,6 +12,7 @@ using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local; using Volo.Abp.EventBus.Local;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.MemoryDb; using Volo.Abp.MemoryDb;
using Volo.Abp.Uow;
namespace Volo.Abp.Domain.Repositories.MemoryDb namespace Volo.Abp.Domain.Repositories.MemoryDb
{ {
@ -65,7 +66,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
return ApplyDataFilters((await GetCollectionAsync()).AsQueryable()); return ApplyDataFilters((await GetCollectionAsync()).AsQueryable());
} }
protected virtual async Task TriggerDomainEventsAsync(object entity) protected virtual void TriggerDomainEvents(object entity)
{ {
var generatesDomainEventsEntity = entity as IGeneratesDomainEvents; var generatesDomainEventsEntity = entity as IGeneratesDomainEvents;
if (generatesDomainEventsEntity == null) if (generatesDomainEventsEntity == null)
@ -78,7 +79,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
{ {
foreach (var localEvent in localEvents) foreach (var localEvent in localEvents)
{ {
await LocalEventBus.PublishAsync(localEvent.GetType(), localEvent); UnitOfWorkManager.Current?.AddOrReplaceLocalEvent(new UnitOfWorkEventRecord(localEvent.GetType(), localEvent, EventOrderGenerator.GetNext()));
} }
generatesDomainEventsEntity.ClearLocalEvents(); generatesDomainEventsEntity.ClearLocalEvents();
@ -89,7 +90,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
{ {
foreach (var distributedEvent in distributedEvents) foreach (var distributedEvent in distributedEvents)
{ {
await DistributedEventBus.PublishAsync(distributedEvent.GetType(), distributedEvent); UnitOfWorkManager.Current?.AddOrReplaceDistributedEvent(new UnitOfWorkEventRecord(distributedEvent.GetType(), distributedEvent, EventOrderGenerator.GetNext()));
} }
generatesDomainEventsEntity.ClearDistributedEvents(); generatesDomainEventsEntity.ClearDistributedEvents();
@ -143,37 +144,37 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
AuditPropertySetter.SetDeletionProperties(entity); AuditPropertySetter.SetDeletionProperties(entity);
} }
protected virtual async Task TriggerEntityCreateEvents(TEntity entity) protected virtual void TriggerEntityCreateEvents(TEntity entity)
{ {
await EntityChangeEventHelper.TriggerEntityCreatingEventAsync(entity); EntityChangeEventHelper.PublishEntityCreatingEvent(entity);
await EntityChangeEventHelper.TriggerEntityCreatedEventAsync(entity); EntityChangeEventHelper.PublishEntityCreatedEvent(entity);
} }
protected virtual async Task TriggerEntityUpdateEventsAsync(TEntity entity) protected virtual void TriggerEntityUpdateEvents(TEntity entity)
{ {
await EntityChangeEventHelper.TriggerEntityUpdatingEventAsync(entity); EntityChangeEventHelper.PublishEntityUpdatingEvent(entity);
await EntityChangeEventHelper.TriggerEntityUpdatedEventAsync(entity); EntityChangeEventHelper.PublishEntityUpdatedEvent(entity);
} }
protected virtual async Task TriggerEntityDeleteEventsAsync(TEntity entity) protected virtual void TriggerEntityDeleteEvents(TEntity entity)
{ {
await EntityChangeEventHelper.TriggerEntityDeletingEventAsync(entity); EntityChangeEventHelper.PublishEntityDeletingEvent(entity);
await EntityChangeEventHelper.TriggerEntityDeletedEventAsync(entity); EntityChangeEventHelper.PublishEntityDeletedEvent(entity);
} }
protected virtual async Task ApplyAbpConceptsForAddedEntityAsync(TEntity entity) protected virtual void ApplyAbpConceptsForAddedEntity(TEntity entity)
{ {
CheckAndSetId(entity); CheckAndSetId(entity);
SetCreationAuditProperties(entity); SetCreationAuditProperties(entity);
await TriggerEntityCreateEvents(entity); TriggerEntityCreateEvents(entity);
await TriggerDomainEventsAsync(entity); TriggerDomainEvents(entity);
} }
protected virtual async Task ApplyAbpConceptsForDeletedEntityAsync(TEntity entity) protected virtual void ApplyAbpConceptsForDeletedEntity(TEntity entity)
{ {
SetDeletionAuditProperties(entity); SetDeletionAuditProperties(entity);
await TriggerEntityDeleteEventsAsync(entity); TriggerEntityDeleteEvents(entity);
await TriggerDomainEventsAsync(entity); TriggerDomainEvents(entity);
} }
public override async Task<TEntity> FindAsync( public override async Task<TEntity> FindAsync(
@ -199,7 +200,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
bool autoSave = false, bool autoSave = false,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
await ApplyAbpConceptsForAddedEntityAsync(entity); ApplyAbpConceptsForAddedEntity(entity);
(await GetCollectionAsync()).Add(entity); (await GetCollectionAsync()).Add(entity);
@ -216,14 +217,14 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
if (entity is ISoftDelete softDeleteEntity && softDeleteEntity.IsDeleted) if (entity is ISoftDelete softDeleteEntity && softDeleteEntity.IsDeleted)
{ {
SetDeletionAuditProperties(entity); SetDeletionAuditProperties(entity);
await TriggerEntityDeleteEventsAsync(entity); TriggerEntityDeleteEvents(entity);
} }
else else
{ {
await TriggerEntityUpdateEventsAsync(entity); TriggerEntityUpdateEvents(entity);
} }
await TriggerDomainEventsAsync(entity); TriggerDomainEvents(entity);
(await GetCollectionAsync()).Update(entity); (await GetCollectionAsync()).Update(entity);
@ -235,7 +236,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
bool autoSave = false, bool autoSave = false,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
await ApplyAbpConceptsForDeletedEntityAsync(entity); ApplyAbpConceptsForDeletedEntity(entity);
if (entity is ISoftDelete softDeleteEntity && !IsHardDeleted(entity)) if (entity is ISoftDelete softDeleteEntity && !IsHardDeleted(entity))
{ {

53
framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs

@ -17,6 +17,7 @@ using Volo.Abp.EventBus.Local;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.MongoDB; using Volo.Abp.MongoDB;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace Volo.Abp.Domain.Repositories.MongoDB namespace Volo.Abp.Domain.Repositories.MongoDB
{ {
@ -182,14 +183,14 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
if (entity is ISoftDelete softDeleteEntity && softDeleteEntity.IsDeleted) if (entity is ISoftDelete softDeleteEntity && softDeleteEntity.IsDeleted)
{ {
SetDeletionAuditProperties(entity); SetDeletionAuditProperties(entity);
await TriggerEntityDeleteEventsAsync(entity); TriggerEntityDeleteEvents(entity);
} }
else else
{ {
await TriggerEntityUpdateEventsAsync(entity); TriggerEntityUpdateEvents(entity);
} }
await TriggerDomainEventsAsync(entity); TriggerDomainEvents(entity);
var oldConcurrencyStamp = SetNewConcurrencyStamp(entity); var oldConcurrencyStamp = SetNewConcurrencyStamp(entity);
ReplaceOneResult result; ReplaceOneResult result;
@ -235,14 +236,14 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
if (isSoftDeleteEntity) if (isSoftDeleteEntity)
{ {
SetDeletionAuditProperties(entity); SetDeletionAuditProperties(entity);
await TriggerEntityDeleteEventsAsync(entity); TriggerEntityDeleteEvents(entity);
} }
else else
{ {
await TriggerEntityUpdateEventsAsync(entity); TriggerEntityUpdateEvents(entity);
} }
await TriggerDomainEventsAsync(entity); TriggerDomainEvents(entity);
SetNewConcurrencyStamp(entity); SetNewConcurrencyStamp(entity);
} }
@ -295,7 +296,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && !IsHardDeleted(entity)) if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && !IsHardDeleted(entity))
{ {
((ISoftDelete)entity).IsDeleted = true; ((ISoftDelete)entity).IsDeleted = true;
await ApplyAbpConceptsForDeletedEntityAsync(entity); ApplyAbpConceptsForDeletedEntity(entity);
ReplaceOneResult result; ReplaceOneResult result;
@ -324,7 +325,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
} }
else else
{ {
await ApplyAbpConceptsForDeletedEntityAsync(entity); ApplyAbpConceptsForDeletedEntity(entity);
DeleteResult result; DeleteResult result;
@ -374,7 +375,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
hardDeletedEntities.Add(entity); hardDeletedEntities.Add(entity);
} }
await ApplyAbpConceptsForDeletedEntityAsync(entity); ApplyAbpConceptsForDeletedEntity(entity);
} }
var dbContext = await GetDbContextAsync(cancellationToken); var dbContext = await GetDbContextAsync(cancellationToken);
@ -573,33 +574,33 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
{ {
CheckAndSetId(entity); CheckAndSetId(entity);
SetCreationAuditProperties(entity); SetCreationAuditProperties(entity);
await TriggerEntityCreateEvents(entity); TriggerEntityCreateEvents(entity);
await TriggerDomainEventsAsync(entity); TriggerDomainEvents(entity);
} }
private async Task TriggerEntityCreateEvents(TEntity entity) private void TriggerEntityCreateEvents(TEntity entity)
{ {
await EntityChangeEventHelper.TriggerEntityCreatingEventAsync(entity); EntityChangeEventHelper.PublishEntityCreatingEvent(entity);
await EntityChangeEventHelper.TriggerEntityCreatedEventAsync(entity); EntityChangeEventHelper.PublishEntityCreatedEvent(entity);
} }
protected virtual async Task TriggerEntityUpdateEventsAsync(TEntity entity) protected virtual void TriggerEntityUpdateEvents(TEntity entity)
{ {
await EntityChangeEventHelper.TriggerEntityUpdatingEventAsync(entity); EntityChangeEventHelper.PublishEntityUpdatingEvent(entity);
await EntityChangeEventHelper.TriggerEntityUpdatedEventAsync(entity); EntityChangeEventHelper.PublishEntityUpdatedEvent(entity);
} }
protected virtual async Task ApplyAbpConceptsForDeletedEntityAsync(TEntity entity) protected virtual void ApplyAbpConceptsForDeletedEntity(TEntity entity)
{ {
SetDeletionAuditProperties(entity); SetDeletionAuditProperties(entity);
await TriggerEntityDeleteEventsAsync(entity); TriggerEntityDeleteEvents(entity);
await TriggerDomainEventsAsync(entity); TriggerDomainEvents(entity);
} }
protected virtual async Task TriggerEntityDeleteEventsAsync(TEntity entity) protected virtual void TriggerEntityDeleteEvents(TEntity entity)
{ {
await EntityChangeEventHelper.TriggerEntityDeletingEventAsync(entity); EntityChangeEventHelper.PublishEntityDeletingEvent(entity);
await EntityChangeEventHelper.TriggerEntityDeletedEventAsync(entity); EntityChangeEventHelper.PublishEntityDeletedEvent(entity);
} }
protected virtual void CheckAndSetId(TEntity entity) protected virtual void CheckAndSetId(TEntity entity)
@ -639,7 +640,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
AuditPropertySetter.SetDeletionProperties(entity); AuditPropertySetter.SetDeletionProperties(entity);
} }
protected virtual async Task TriggerDomainEventsAsync(object entity) protected virtual void TriggerDomainEvents(object entity)
{ {
var generatesDomainEventsEntity = entity as IGeneratesDomainEvents; var generatesDomainEventsEntity = entity as IGeneratesDomainEvents;
if (generatesDomainEventsEntity == null) if (generatesDomainEventsEntity == null)
@ -652,7 +653,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
{ {
foreach (var localEvent in localEvents) foreach (var localEvent in localEvents)
{ {
await LocalEventBus.PublishAsync(localEvent.GetType(), localEvent); UnitOfWorkManager.Current?.AddOrReplaceLocalEvent(new UnitOfWorkEventRecord(localEvent.GetType(), localEvent, EventOrderGenerator.GetNext()));
} }
generatesDomainEventsEntity.ClearLocalEvents(); generatesDomainEventsEntity.ClearLocalEvents();
@ -663,7 +664,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
{ {
foreach (var distributedEvent in distributedEvents) foreach (var distributedEvent in distributedEvents)
{ {
await DistributedEventBus.PublishAsync(distributedEvent.GetType(), distributedEvent); UnitOfWorkManager.Current?.AddOrReplaceDistributedEvent(new UnitOfWorkEventRecord(distributedEvent.GetType(), distributedEvent, EventOrderGenerator.GetNext()));
} }
generatesDomainEventsEntity.ClearDistributedEvents(); generatesDomainEventsEntity.ClearDistributedEvents();

13
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/AmbientUnitOfWork.cs

@ -19,5 +19,18 @@ namespace Volo.Abp.Uow
{ {
_currentUow.Value = unitOfWork; _currentUow.Value = unitOfWork;
} }
public IUnitOfWork GetCurrentByChecking()
{
var uow = UnitOfWork;
//Skip reserved unit of work
while (uow != null && (uow.IsReserved || uow.IsDisposed || uow.IsCompleted))
{
uow = uow.Outer;
}
return uow;
}
} }
} }

14
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/EventOrderGenerator.cs

@ -0,0 +1,14 @@
using System.Threading;
namespace Volo.Abp.Uow
{
public static class EventOrderGenerator
{
private static long _lastOrder;
public static long GetNext()
{
return Interlocked.Increment(ref _lastOrder);
}
}
}

2
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IAmbientUnitOfWork.cs

@ -2,6 +2,6 @@
{ {
public interface IAmbientUnitOfWork : IUnitOfWorkAccessor public interface IAmbientUnitOfWork : IUnitOfWorkAccessor
{ {
IUnitOfWork GetCurrentByChecking();
} }
} }

4
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs

@ -139,7 +139,7 @@ namespace Volo.Abp.Uow
{ {
if (LocalEvents.Any()) if (LocalEvents.Any())
{ {
var localEventsToBePublished = LocalEvents.ToArray(); var localEventsToBePublished = LocalEvents.OrderBy(e => e.EventOrder).ToArray();
LocalEvents.Clear(); LocalEvents.Clear();
await UnitOfWorkEventPublisher.PublishLocalEventsAsync( await UnitOfWorkEventPublisher.PublishLocalEventsAsync(
localEventsToBePublished localEventsToBePublished
@ -148,7 +148,7 @@ namespace Volo.Abp.Uow
if (DistributedEvents.Any()) if (DistributedEvents.Any())
{ {
var distributedEventsToBePublished = DistributedEvents.ToArray(); var distributedEventsToBePublished = DistributedEvents.OrderBy(e => e.EventOrder).ToArray();
DistributedEvents.Clear(); DistributedEvents.Clear();
await UnitOfWorkEventPublisher.PublishDistributedEventsAsync( await UnitOfWorkEventPublisher.PublishDistributedEventsAsync(
distributedEventsToBePublished distributedEventsToBePublished

6
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkEventRecord.cs

@ -9,6 +9,8 @@ namespace Volo.Abp.Uow
public Type EventType { get; } public Type EventType { get; }
public long EventOrder { get; }
/// <summary> /// <summary>
/// Extra properties can be used if needed. /// Extra properties can be used if needed.
/// </summary> /// </summary>
@ -16,10 +18,12 @@ namespace Volo.Abp.Uow
public UnitOfWorkEventRecord( public UnitOfWorkEventRecord(
Type eventType, Type eventType,
object eventData) object eventData,
long eventOrder)
{ {
EventType = eventType; EventType = eventType;
EventData = eventData; EventData = eventData;
EventOrder = eventOrder;
} }
} }
} }

15
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkManager.cs

@ -10,7 +10,7 @@ namespace Volo.Abp.Uow
[Obsolete("This will be removed in next versions.")] [Obsolete("This will be removed in next versions.")]
public static AsyncLocal<bool> DisableObsoleteDbContextCreationWarning { get; } = new AsyncLocal<bool>(); public static AsyncLocal<bool> DisableObsoleteDbContextCreationWarning { get; } = new AsyncLocal<bool>();
public IUnitOfWork Current => GetCurrentUnitOfWork(); public IUnitOfWork Current => _ambientUnitOfWork.GetCurrentByChecking();
private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly IAmbientUnitOfWork _ambientUnitOfWork; private readonly IAmbientUnitOfWork _ambientUnitOfWork;
@ -86,19 +86,6 @@ namespace Volo.Abp.Uow
return true; return true;
} }
private IUnitOfWork GetCurrentUnitOfWork()
{
var uow = _ambientUnitOfWork.UnitOfWork;
//Skip reserved unit of work
while (uow != null && (uow.IsReserved || uow.IsDisposed || uow.IsCompleted))
{
uow = uow.Outer;
}
return uow;
}
private IUnitOfWork CreateNewUnitOfWork() private IUnitOfWork CreateNewUnitOfWork()
{ {
var scope = _serviceScopeFactory.CreateScope(); var scope = _serviceScopeFactory.CreateScope();

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

@ -30,19 +30,65 @@ namespace Volo.Abp.TestApp.Testing
[Fact] [Fact]
public virtual async Task Should_Publish_Events_In_Order() public virtual async Task Should_Publish_Events_In_Order()
{ {
bool entityCreatedEventHandled = false; bool testPersonCreateHandled = false;
bool douglesUpdateHandled = false;
bool douglesNameChangeHandled = false;
bool customEventHandled = false;
bool customEvent2Handled = false;
LocalEventBus.Subscribe<EntityCreatedEventData<Person>>(data => LocalEventBus.Subscribe<EntityCreatedEventData<Person>>(data =>
{ {
data.Entity.Name.ShouldBe("TestPerson1"); data.Entity.Name.ShouldBe("TestPerson1");
entityCreatedEventHandled = true; testPersonCreateHandled = true;
douglesUpdateHandled.ShouldBeFalse();
douglesNameChangeHandled.ShouldBeFalse();
customEventHandled.ShouldBeFalse();
customEvent2Handled.ShouldBeFalse();
return Task.CompletedTask; return Task.CompletedTask;
}); });
LocalEventBus.Subscribe<MyCustomEventData>(data => LocalEventBus.Subscribe<MyCustomEventData>(data =>
{ {
data.Value.ShouldBe("42"); data.Value.ShouldBe("42");
entityCreatedEventHandled.ShouldBe(true); customEventHandled = true;
testPersonCreateHandled.ShouldBeTrue();
douglesUpdateHandled.ShouldBeFalse();
douglesNameChangeHandled.ShouldBeFalse();
customEvent2Handled.ShouldBeFalse();
return Task.CompletedTask;
});
LocalEventBus.Subscribe<PersonNameChangedEvent>(data =>
{
data.OldName.ShouldBe("Douglas");
data.Person.Name.ShouldBe("Douglas-Updated");
douglesNameChangeHandled = true;
testPersonCreateHandled.ShouldBeTrue();
customEventHandled.ShouldBeTrue();
douglesUpdateHandled.ShouldBeFalse();
customEvent2Handled.ShouldBeFalse();
return Task.CompletedTask;
});
LocalEventBus.Subscribe<EntityUpdatedEventData<Person>>(data =>
{
data.Entity.Name.ShouldBe("Douglas-Updated");
douglesUpdateHandled = true;
testPersonCreateHandled.ShouldBeTrue();
customEventHandled.ShouldBeTrue();
douglesNameChangeHandled.ShouldBeTrue();
customEvent2Handled.ShouldBeFalse();
return Task.CompletedTask;
});
LocalEventBus.Subscribe<MyCustomEventData2>(data =>
{
data.Value.ShouldBe("44");
customEvent2Handled = true;
testPersonCreateHandled.ShouldBeTrue();
customEventHandled.ShouldBeTrue();
douglesUpdateHandled.ShouldBeTrue();
douglesNameChangeHandled.ShouldBeTrue();
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -53,6 +99,12 @@ namespace Volo.Abp.TestApp.Testing
); );
await LocalEventBus.PublishAsync(new MyCustomEventData { Value = "42" }); await LocalEventBus.PublishAsync(new MyCustomEventData { Value = "42" });
var douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId);
douglas.ChangeName("Douglas-Updated");
await PersonRepository.UpdateAsync(douglas);
await LocalEventBus.PublishAsync(new MyCustomEventData2 { Value = "44" });
}); });
} }
@ -125,5 +177,10 @@ namespace Volo.Abp.TestApp.Testing
{ {
public string Value { get; set; } public string Value { get; set; }
} }
private class MyCustomEventData2
{
public string Value { get; set; }
}
} }
} }

14
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs

@ -88,12 +88,16 @@ namespace Volo.Abp.TestApp.Testing
[Fact] [Fact]
public async Task Multiple_Update_Should_Result_With_Single_Updated_Event_In_The_Same_Uow() public async Task Multiple_Update_Should_Result_With_Single_Updated_Event_In_The_Same_Uow()
{ {
var personId = Guid.NewGuid(); var createEventCount = 0;
await PersonRepository.InsertAsync(new Person(personId, Guid.NewGuid().ToString("D"), 42));
var updateEventCount = 0; var updateEventCount = 0;
var updatedAge = 0; var updatedAge = 0;
DistributedEventBus.Subscribe<EntityCreatedEto<PersonEto>>(eto =>
{
createEventCount++;
return Task.CompletedTask;
});
DistributedEventBus.Subscribe<EntityUpdatedEto<PersonEto>>(eto => DistributedEventBus.Subscribe<EntityUpdatedEto<PersonEto>>(eto =>
{ {
updateEventCount++; updateEventCount++;
@ -101,6 +105,9 @@ namespace Volo.Abp.TestApp.Testing
return Task.CompletedTask; return Task.CompletedTask;
}); });
var personId = Guid.NewGuid();
await PersonRepository.InsertAsync(new Person(personId, Guid.NewGuid().ToString("D"), 42));
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin()) using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{ {
var person = await PersonRepository.GetAsync(personId); var person = await PersonRepository.GetAsync(personId);
@ -120,6 +127,7 @@ namespace Volo.Abp.TestApp.Testing
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
createEventCount.ShouldBe(1);
updateEventCount.ShouldBe(1); updateEventCount.ShouldBe(1);
updatedAge.ShouldBe(45); updatedAge.ShouldBe(45);
} }

Loading…
Cancel
Save