如何在CQRS模式中为Identity用户表触发领域事件?
问题描述
我需要在身份事件(比如添加用户、使用UserManager操作)发生时触发领域事件,其他继承自BaseEntity的实体已经成功实现了这个功能,但我的ApplicationUser继承自IdentityUser,无法同时继承BaseEntity,请问该如何结合两者实现事件触发?
现有代码示例:
其他实体的实现(正常工作)
public class Vehicle : BaseEntity { // ... 实体属性 }
BaseEntity代码
public abstract class BaseEntity { 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(); } }
无法实现的ApplicationUser
public class ApplicationUser : IdentityUser { public string FirstName { get; set; } public string LastName { get; set; } public string FatherName { get; set; } }
解决方案
由于C#不支持多继承,我们可以通过以下两种主流方案实现需求:
方案1:提取领域事件逻辑为独立类,通过组合复用
把BaseEntity中的领域事件管理逻辑封装成独立类,同时让BaseEntity和ApplicationUser都依赖这个类,既保证代码复用,又不破坏原有继承结构。
第一步:封装事件管理类
public class DomainEventCollection { private readonly List<BaseEvent> _domainEvents = new(); 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(); } }
第二步:修改BaseEntity复用该类
public abstract class BaseEntity { public string Id { get; set; } private readonly DomainEventCollection _eventCollection = new(); [NotMapped] public IReadOnlyCollection<BaseEvent> DomainEvents => _eventCollection.DomainEvents; public void AddDomainEvent(BaseEvent domainEvent) { _eventCollection.AddDomainEvent(domainEvent); } public void RemoveDomainEvent(BaseEvent domainEvent) { _eventCollection.RemoveDomainEvent(domainEvent); } public void ClearDomainEvents() { _eventCollection.ClearDomainEvents(); } }
第三步:在ApplicationUser中集成事件管理类
public class ApplicationUser : IdentityUser { public string FirstName { get; set; } public string LastName { get; set; } public string FatherName { get; set; } private readonly DomainEventCollection _eventCollection = new(); [NotMapped] public IReadOnlyCollection<BaseEvent> DomainEvents => _eventCollection.DomainEvents; public void AddDomainEvent(BaseEvent domainEvent) { _eventCollection.AddDomainEvent(domainEvent); } public void RemoveDomainEvent(BaseEvent domainEvent) { _eventCollection.RemoveDomainEvent(domainEvent); } public void ClearDomainEvents() { _eventCollection.ClearDomainEvents(); } }
方案2:定义领域事件接口,让ApplicationUser实现接口
先抽离事件操作的标准接口,让BaseEntity和ApplicationUser分别实现该接口,后续可统一通过接口处理所有实体的领域事件。
第一步:定义事件接口
public interface IDomainEventEntity { IReadOnlyCollection<BaseEvent> DomainEvents { get; } void AddDomainEvent(BaseEvent domainEvent); void RemoveDomainEvent(BaseEvent domainEvent); void ClearDomainEvents(); }
第二步:修改BaseEntity实现接口
public abstract class BaseEntity : IDomainEventEntity { 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(); } }
第三步:让ApplicationUser实现接口
public class ApplicationUser : IdentityUser, IDomainEventEntity { public string FirstName { get; set; } public string LastName { get; set; } public string FatherName { 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(); } }
事件触发时机示例
可以在UserManager操作完成后手动添加事件,或者自定义UserManager自动触发:
手动触发示例
var user = new ApplicationUser { /* 设置用户属性 */ }; var result = await _userManager.CreateAsync(user); if (result.Succeeded) { user.AddDomainEvent(new UserCreatedEvent(user.Id, user.Email)); // 执行事件总线发布或其他处理逻辑 }
自定义UserManager自动触发
public class ApplicationUserManager : UserManager<ApplicationUser> { public ApplicationUserManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators, IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<ApplicationUser>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) { } public override async Task<IdentityResult> CreateAsync(ApplicationUser user) { var result = await base.CreateAsync(user); if (result.Succeeded) { user.AddDomainEvent(new UserCreatedEvent(user.Id, user.Email)); } return result; } }
内容的提问来源于stack exchange,提问作者irancss
相关产品推荐
相关产品推荐

