[英]Why Not returning value after AddDomainEvent in CQRS pattern
I use CQRS pattern with MediatR in my project.In 2 parts, it doesn't end the way I expect.我在我的项目中使用 CQRS 模式和 MediatR。在 2 个部分中,它并没有像我期望的那样结束。
1: When I want to return a value after changes with a command,I save the changes by using (for example: usermanager), that's why the created view model is no longer returned and it is sent to the event push path. 1:当我想用命令改变后返回一个值时,我使用(例如:usermanager)保存更改,所以创建的视图model不再返回,而是发送到事件推送路径。
BaseEntity:基础实体:
public abstract class BaseEntity : IBaseId<string>, IEntityWithDomainEvent
{
public string Id { get; set; }
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();
}
}
public class LoginUserCommand : IRequest<ResponseLoginViewModel>
{
public string PhoneNumber { get; set; }
public string VerifyCode { get; set; }
public string ReturnUrl { get; set; }
}
public class LoginUserCommandHandler : IRequestHandler<LoginUserCommand, ResponseLoginViewModel>
{
private readonly IIdentityService _identityService;
public LoginUserCommandHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<ResponseLoginViewModel> Handle(LoginUserCommand request, CancellationToken cancellationToken)
{
var user = await _identityService.GetUserWithMobileAsync(request.PhoneNumber);
if (user.VerifyCode == request.VerifyCode)
{
var res = await _identityService.LoginUser(user);
user.RefreshToken = res.RefreshToken;
user.RefreshTokenExpiryTime = res.Expiration;
user.AddDomainEvent(new DriverLoggedInEvent(user));
user.VerifyCode = await _identityService.GenerateRandomCode();
//TODO: Here If I use _usermanager.updateuser it no longer returns to (return res) and
goes to the DriverLoggedInEventHandler class
if (string.IsNullOrEmpty(res.Token))
{
return new ResponseLoginViewModel();
}
return res;
}
return new ResponseLoginViewModel();
}
}
2: My َApplicationUser class inherits both from IdentityUser and from the interface I created called: IEntityWithDomainEvent. 2:我的 ApplicationUser class 继承自 IdentityUser 和我创建的接口:IEntityWithDomainEvent。
public interface IEntityWithDomainEvent
{
IReadOnlyCollection<BaseEvent> DomainEvents { get; }
void AddDomainEvent(BaseEvent domainEvent);
void RemoveDomainEvent(BaseEvent domainEvent);
void ClearDomainEvents();
}
But I can't put.Entries() at the time of savechange, so I've passed it in the stupidest way for now.但是我不能在savechange的时候put.Entries(),所以我现在用最愚蠢的方式传递了它。
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
try
{
await _mediator.DispatchDomainEvents(this);
return await base.SaveChangesAsync(cancellationToken);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
throw new Exception(e.Message);
}
}
DispatchDomainEvent:调度域事件:
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());
// TODO : Problem two is solved for now by the following silly method
if (domainEvents.Count == 0)
{
var entities2 = context.ChangeTracker
.Entries<ApplicationUser>()
.Where(e => e.Entity.DomainEvents.Any())
.Select(e => e.Entity);
domainEvents = entities2
.SelectMany(e => e.DomainEvents)
.ToList();
entities2.ToList().ForEach(e => e.ClearDomainEvents());
}
foreach (var domainEvent in domainEvents)
await mediator.Publish(domainEvent);
}
}
Thank you in advance for your help预先感谢您的帮助
Update1: In LoginUserCommandHandler: Please read the TODO section. Update1:在 LoginUserCommandHandler 中:请阅读 TODO 部分。 The problem is in the DispatchDomainEvent section in the TODO section.
问题出在 TODO 部分的 DispatchDomainEvent 部分。 After update user with _usermanager.updateuser in LoginUserCommandHandler, because the database is saved, that's why the value of return res in LoginUserCommandHandler is not returned
LoginUserCommandHandler中用_usermanager.updateuser更新用户后,因为数据库保存了,所以LoginUserCommandHandler中return res的值没有返回
Not sure what actual problems are but as for DispatchDomainEvents
it seems that BaseEntity
should implement IEntityWithDomainEvent
and if it does - you can just use the interface simplifying the implementation:不确定实际问题是什么,但至于
DispatchDomainEvents
似乎BaseEntity
应该实现IEntityWithDomainEvent
如果它实现了 - 你可以使用简化实现的接口:
public static async Task DispatchDomainEvents(this IMediator mediator, DbContext context)
{
var entities = context.ChangeTracker
.Entries<IEntityWithDomainEvent>()
.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)
await mediator.Publish(domainEvent);
}
Also possibly double enumeration of entities
is not ideal, so: entities
的双重枚举也可能不理想,因此:
public static async Task DispatchDomainEvents(this IMediator mediator, DbContext context)
{
var entities = context.ChangeTracker
.Entries<IEntityWithDomainEvent>()
.Where(e => e.Entity.DomainEvents.Any())
.Select(e => e.Entity);
foreach (var entity in entities)
{
foreach (var domainEvent in entity.DomainEvents)
{
await mediator.Publish(domainEvent);
}
entity.ClearDomainEvents();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.