有 3 个项目:Core、Persistence(EfCore with SQL Server)和 UI(Razor Pages)。
当用户更新记录时,DomainEvent 会添加到 Core 项目中,并在提交时在持久层中发布,但此时 User 不再存在于 HttpContext 中,并且服务器会抛出错误,因为 User 为 null。
添加新群组成员时会添加事件 MemberAddedEvent。
public class Group
{
public List<Contact> Members { get; set; }
public List<DomainEvent> DomainEvents { get; private set; }
public void AddMember(Contact newMember)
{
Members.Add(newMember);
var memberAddedEvent = new MemberAddedEvent(newMember);
DomainEvents.Add(memberAddedEvent);
}
}
该事件在保存时发布在 DbContext 中:
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken
= new CancellationToken())
{
/*
truncated for brevity
*/
var entitiesWithEvents = ChangeTracker
.Entries()
.Select(e => e.Entity as BaseEntity<int>)
.Where(e => e?.DomainEvents != null && e.DomainEvents.Any())
.ToArray();
foreach (var entity in entitiesWithEvents)
{
var events = entity.DomainEvents.ToArray();
entity.DomainEvents.Clear();
foreach (var domainEvent in events)
{
await _mediator.Publish(domainEvent, cancellationToken).ConfigureAwait(false);
}
}
return result;
}
UI 有一个服务 IUserAccesssor,它返回 HttpContext.User.Identity.Name。
internal class UserAccessorService(IHttpContextAccessor context) : IUserAccessorService
{
public string GetUserName()
{
return context.HttpContext.User.Identity.Name;
}
}
MemberAddedEventHandler 将通知消息传递到 SignalR MessageHub,以向用户显示漂亮的小 toast 通知。
大多数情况下,这种情况发生得足够快,用户仍然存在于 HttpContextr 中,但有时则不然。
如何保留用户名?我是否需要将其添加到对 AddMember 的初始请求中并将其传递给 DomainEvent?
public class AddMemberCommand : IRequest<AddMemberCommandResponse>
{
/*
member properties here
*/
public string UserName { get; set; }
}
public abstract class DomainEvent : INotification
{
public DomainEvent(string userName)
{
UserName = userName;
}
public string UserName { get; private set; }
}
目前我已经在持久层解决了这个问题。我向 DomainEvent 抽象类添加了一个 User 属性。
public abstract class DomainEvent : INotification
{
public string User { get; set; }
}
我已经在跟踪用户名以记录谁对数据库中的记录进行了更改
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken
= new CancellationToken())
{
/*
truncated for brevity
*/
var entitiesWithEvents = ChangeTracker
.Entries()
.Select(e => e.Entity as BaseEntity<int>)
.Where(e => e?.DomainEvents != null && e.DomainEvents.Any())
.ToArray();
foreach (var entity in entitiesWithEvents)
{
var events = entity.DomainEvents.ToArray();
entity.DomainEvents.Clear();
foreach (var domainEvent in events)
{
/*
User Name added here to the Domain Event
*/
domainEvent.User = userService.UserName();
await _mediator.Publish(domainEvent, cancellationToken).ConfigureAwait(false);
}
}
return result;
}
然后由EventHandler读取并传递给通知服务
internal class MemberAddedEventHandler(INotifyService notifyService) : INotificationHandler<MemberAddedEvent>
{
public async Task Handle(MemberAddedEvent notification, CancellationToken cancellationToken)
{
NotificationMessage message = new NotificationMessage(
user: notification.User,
message: $"Member added: {notification.Member.FirstName}",
state: "Success"
);
await notifyService.NotifyUserAsync(message);
}
}