You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 00:16:12