首页 .Net ABP使用领域事件

ABP使用领域事件

这个用例比较简单,没有太多的复杂逻辑,按照我们传统的思路,直接在任务编辑方法中添加邮件和消息发送的方法即可,代码如下:

public void UpdateTask(UpdateTaskInput input)
{
    //We can use Logger, it's defined in ApplicationService base >.Info("Updating a task for input: " + input);

    //获取是否有权限
    bool canAssignTaskToOther = PermissionChecker.IsGranted(PermissionNames.Pages_Tasks_AssignPerson);
    //如果任务已经分配且未分配给自己,且不具有分配任务权限,则抛出异常
    if (input.AssignedPersonId.HasValue && input.AssignedPersonId.Value != AbpSession.GetUserId() &&
        !canAssignTaskToOther)
    {
        throw new AbpAuthorizationException("没有分配任务给他人的权限!");
    }

    var updateTask = Mapper.Map<Task>(input);
    var user = _userRepository.Get(input.AssignedPersonId.Value);
    //先执行分配任务
    _taskManager.AssignTaskToPerson(updateTask, user);

    //再更新其他字段
    _taskRepository.Update(updateTask);

    //发送通知
    var message = "You hava been assigned one task into your todo list.";
    _smtpEmailSender.Send("ysjshengjie@qq.com", updateTask.AssignedPerson.EmailAddress, "New Todo item", message);

    _notificationPublisher.Publish("NewTask", new MessageNotificationData(message), null,
        NotificationSeverity.Info, new[] { updateTask.AssignedPerson.ToUserIdentifier() });
}
更新任务出错

运行,直接挂掉。原因是很清楚,是由于邮箱配置有误导致。但是我们思考一下。我们进行任务分配时最关注的是任务被成功分配,而至于通知是否成功发送相对来说是次要的。但是现在却由于通知发送失败导致任务无法被成功分配,这是不合理的。

那我们要如何做呢?当然是拆分业务逻辑。而这时领域事件就可以粉墨登场了。

3.使用领域事件

就这个用例而言,“用户被成功分配任务”就是一个领域事件。下面我们就来实际应用一下。

3.1. 定义事件源

一个领域事件是通过事件源来识别的,我们直接定义一个TaskAssignedEventData继承自EventData即可:

public >: EventData
{
    public User User { get; set; }
    public Task Task { get; set; }
    public TaskAssignedEventData(Task task, User user)
    {
        this.Task = task;
        this.User = user;
    }
}

3.2. 实现事件处理

定义TaskAssignedToUser事件处理,实现IEventHandler<TaskAssignedEventData>泛型接口即可:

public >TaskAssignedToUser : IEventHandler<TaskAssignedEventData>, ITransientDependency
{
    private readonly ISmtpEmailSender _smtpEmailSender;
    private readonly INotificationPublisher _notificationPublisher;
    public TaskAssignedToUser(ISmtpEmailSender smtpEmailSender, INotificationPublisher notificationPublisher)
    {
        _smtpEmailSender = smtpEmailSender;
        _notificationPublisher = notificationPublisher;
    }
    public void HandleEvent(TaskAssignedEventData eventData)
    {
        var message = "You hava been assigned one task into your todo list.";
        //TODO:需要重新配置QQ邮箱密码
        _smtpEmailSender.Send("ysjshengjie@qq.com", eventData.Task.AssignedPerson.EmailAddress, "New Todo item", message);

        _notificationPublisher.Publish("NewTask", new MessageNotificationData(message), null,
                    NotificationSeverity.Info, new[] { eventData.User.ToUserIdentifier() });
    }
}

3.3. 事件触发

我们可以直接在上一节定义的TaskManager领域服务中触发领域事件。因为这样更符合当前领域事件通用语言的表述。

//TaskManager.cs
public void AssignTaskToPerson(Task task, User user)
{
    //已经分配,就不再分配
    if (task.AssignedPersonId.HasValue && task.AssignedPersonId.Value == user.Id)
    {
        return;
    }

    if (task.State != TaskState.Open)
    {
        throw new ApplicationException("处于非活动状态的任务不能分配!");
    }

    task.AssignedPersonId = user.Id;

    //使用领域事件触发发送通知操作
    _eventBus.Trigger(new TaskAssignedEventData(task, user));
}

再运行,我们发现虽然没有接收到消息通知(发送失败),但任务却可以成功分配。

4. 一些问题

  1. 领域事件在哪注册(订阅)?
    应用程序启动时Abp根据约定俗成的命名规则将事件源和事件处理注册到了依赖容器中和事件总线维护的容器中。我们也可以自行在应用服务或领域服务中手动注册。
  2. 领域事件在哪触发(发布)?
    事件的触发同样也没有限定,根据需要,可以在应用服务、领域服务、聚合、实体中发布。
  3. 领域事件的命名?
    领域事件的名字要反映出过去发生的事情的概念。

4.最后

由于demo比较简单,找不到合适的用例,以上使用的用例比较简单。在复杂的用例中,当需要更新多个聚合时,领域事件的作用就体现出来了,借助领域事件我们可以很好的进行事务拆分,达到最终一致性的目的。

而至于领域事件衍生出来的事件存储和事件溯源,下次再和大家分享。

特别声明:本站部分内容收集于互联网是出于更直观传递信息的目的。该内容版权归原作者所有,并不代表本站赞同其观点和对其真实性负责。如该内容涉及任何第三方合法权利,请及时与824310991@qq.com联系,我们会及时反馈并处理完毕。