我的一个实体中有一个状态,如果该状态被取消或完成,那么我想调度一个通过 MediatR 发送的事件。如果状态是付款成功,我想发送一封电子邮件,然后继续取消/完成的活动。
实体看起来像这样:
public Order : BaseEntity
{
....
private OrderStatus _status = OrderStatus.InProgress;
public OrderStatus Status
{
get => _status;
set
{
if (SentAt != null)
{
// Already done the workflow, no longer need to do them.
_status = value;
return;
}
if (value is OrderStatus.Cancelled or OrderStatus.Completed)
{
AddDomainEvent(new SendOrderStatusUpdate(this));
}
if (value == OrderStatus.PaymentSuccessful)
{
AddDomainEvent(new SendInvoice(this));
}
_status = value;
}
}
}
但是,当我运行这个时。如果我将订单状态设置为
OrderStatus.PaymentSuccessful
,它会运行整个事件,最后我将状态设置为 OrderStatus.Completed
,它不会运行与该状态相关的域事件。
我的
BaseEntity
课程与推荐的方法相对相同:
public class BaseEntity
{
/// <summary>
/// The identifier for the entity.
/// </summary>
[Key]
public int Id { get; set; }
/// <summary>
/// The domain events.
/// </summary>
private readonly List<BaseEvent> _domainEvents = new();
[NotMapped]
public IReadOnlyCollection<BaseEvent> DomainEvents => _domainEvents.AsReadOnly();
public void AddDomainEvent(BaseEvent domainEvent)
{
_domainEvents.Add(domainEvent);
}
public void RemoveDomainEvent(BaseEvent domainEvent)
{
_domainEvents.Remove(domainEvent);
}
public void ClearDomainEvents()
{
_domainEvents.Clear();
}
}
然后,我扩展 MediatR 以与 Hangfire 一起使用,以便对事件进行排队并在后台执行它们,这也是推荐的方法。
public static class MediatorExtensions
{
public static async Task DispatchDomainEvents(this IMediator mediator, DbContext context)
{
var entities = context.ChangeTracker
.Entries<BaseEntity>()
.Where(e => e.Entity.DomainEvents.Any())
.Select(e => e.Entity);
var domainEvents = entities.SelectMany(e => e.DomainEvents).ToList();
entities.ToList().ForEach(e => e.ClearDomainEvents());
foreach (var domainEvent in domainEvents)
{
var client = new BackgroundJobClient();
client.Enqueue<MediatorBridge>(bridge => bridge.Publish(domainEvent));
}
}
}
我期待辅助域事件会触发,但是,它似乎并没有进入那里。
我最初认为
ClearDomainEvents
方法可能会变得混乱并在触发之前清除域事件。我已确认情况并非如此,事件似乎正在“调度”,但 MediatR INotificationHandler<SendOrderStatusUpdate>
未进入。
将订单状态设置为
OrderStatus.Completed
会立即正确触发该工作流程,因此我觉得这与正在运行的订单及其处理方式有关。
更新:
我已经开始工作了,但并不完全。
我已将调度更新为以下内容:
public static async Task DispatchDomainEvents(this IMediator mediator, DbContext context)
{
var entities = GetEntitiesWithPendingEvents(context);
while (entities.Any())
{
foreach (var entity in entities)
foreach (var domainEvent in entity.DomainEvents.ToList())
{
var client = new BackgroundJobClient();
client.Enqueue<MediatorBridge>(bridge => bridge.Publish(domainEvent));
entity.RemoveDomainEvent(domainEvent);
}
entities = GetEntitiesWithPendingEvents(context);
}
}
private static List<BaseEntity> GetEntitiesWithPendingEvents(DbContext context)
{
return context.ChangeTracker
.Entries<BaseEntity>()
.Where(e => e.Entity.DomainEvents.Any())
.Select(e => e.Entity)
.ToList();
}
当更新在另一个事件处理程序中触发时,它会获取更新。然而,这在立即调用它时有效,即:
_mediator.Publish(domainEvent)
,但在我当前的设置中不起作用,我试图通过 Hangfire 传递它。好像没收到。