Event.Publish - dotnet-shashlik/shashlik.eventbus GitHub Wiki

事件发布

定义事件

事件类需实现接口 IEventIEvent 仅用于约束与规范,没有实际业务意义。事件分为普通事件和延迟事件,区别仅在于发布时是否指定延迟时间。

public class NewUserEvent : IEvent
{
    public string Id { get; set; }
    public string Name { get; set; }
}

public class NewUserPromotionEvent : IEvent
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string PromotionId { get; set; }
}

EventBusNameAttribute

事件名称和事件处理器名称默认规则为 {Type.Name}.{Environment},对应消息队列中的 routingKey/topic、queue/groupId。可通过 [EventBusName] 特性显式声明名称:

[EventBusName("order.created")]
public class OrderCreatedEvent : IEvent
{
    // ...
}

以 RabbitMQ + Production 环境为例:

  • [EventBusName("order.created")] → 路由到 order.created.Production
  • 无特性 → 路由到 OrderCreatedEvent.Production

IEventPublisher

事件发布通过 IEventPublisher 接口:

public interface IEventPublisher
{
    Task PublishAsync<TEvent>(
        TEvent @event,
        ITransactionContext? transactionContext,
        IDictionary<string, string>? additionalItems = null,
        CancellationToken cancellationToken = default
    ) where TEvent : IEvent;

    Task PublishAsync<TEvent>(
        TEvent @event,
        DateTimeOffset delayAt,
        ITransactionContext? transactionContext,
        IDictionary<string, string>? additionalItems = null,
        CancellationToken cancellationToken = default
    ) where TEvent : IEvent;
}

参数说明:

  • TEvent @event:事件实例
  • DateTimeOffset delayAt:延迟执行时间,小于等于当前时间时作为非延迟事件
  • ITransactionContext? transactionContext:事务上下文,不需要事务一致性时传 null
  • IDictionary<string, string>? additionalItems:附加数据,EventBus 默认添加以下 key,请勿重复
    • eventbus-msg-id:消息 ID
    • eventbus-send-at:发送时间
    • eventbus-delay-at:延迟执行时间
    • eventbus-event-name:事件名称

事务集成

最终一致性原理

eventbus

消息数据与业务数据在同一事务中提交或回滚,EventBus 确认消息已提交后才执行真正的发送。

ITransactionContext 事务上下文

public interface ITransactionContext
{
    bool IsDone();
}

已有实现:

  • RelationDbStorageTransactionContext:关系型数据库事务(基于 IDbTransaction
  • XaTransactionContext:XA 分布式事务(基于 TransactionScope
  • MongoDbTransactionContext:MongoDB 事务(基于 IClientSessionHandle

EF Core 扩展(推荐)

引入包 Shashlik.EventBus.Extensions.EfCore,通过 DbContext 直接发布事件,自动共享事务:

using var tran = await DbContext.Database.BeginTransactionAsync();
try
{
    // 业务逻辑...

    // 自动使用 DbContext 当前事务上下文
    await DbContext.PublishEventAsync(new NewUserEvent { Id = user.Id, Name = input.Name });

    // 发布延迟事件
    await DbContext.PublishEventAsync(
        new NewUserPromotionEvent { Id = user.Id, Name = input.Name, PromotionId = "1" },
        DateTimeOffset.Now.AddMinutes(30));

    await tran.CommitAsync();
}
catch
{
    await tran.RollbackAsync();
}

也可以手动获取事务上下文:

var txContext = DbContext.GetTransactionContext();
await EventPublisher.PublishAsync(newEvent, txContext);

FreeSql 事务集成

Shashlik.EventBus.RelationDbStorage 内置 FreeSql 事务上下文扩展,无需额外安装包:

// 同线程事务 (fsql.Transaction(() => ...) 场景)
var txContext = fsql.GetCurrentThreadTransactionContext();

// 从 IUnitOfWork 获取
var txContext = unitOfWork.GetTransactionContextFromUnitOfWork();

// 从 IUnitOfWorkManager 获取
var txContext = unitOfWorkManager.GetTransactionContextFromUnitOfWorkManager();

await EventPublisher.PublishAsync(newEvent, txContext);

SqlSugar 事务集成

引入包 Shashlik.EventBus.Extensions.SqlSugar

// 从 IAdo 获取
var txContext = ado.GetTransactionContext();

// 从 ISqlSugarClient 获取
var txContext = sqlSugarClient.GetTransactionContext();

// 从 ISugarUnitOfWork 获取
var txContext = sugarUnitOfWork.GetTransactionContext();

await EventPublisher.PublishAsync(newEvent, txContext);

XA 事务 (TransactionScope)

using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
try
{
    // 业务逻辑...

    await EventPublisher.PublishAsync(new NewUserEvent { ... }, XaTransactionContext.Current);

    scope.Complete();
}
catch
{
    // 回滚,消息数据也将回滚
}

注意:XaTransactionContext.Current 对事务是否提交的判断延迟到 TransactionScope 的 Dispose 后,因此必须 Dispose TransactionScope

手动构造事务上下文

如果你使用其他 ORM 或直接操作 IDbTransaction,可以手动构造:

using var conn = new MySqlConnection("...");
await conn.OpenAsync();
using var tran = await conn.BeginTransactionAsync();
try
{
    // 业务逻辑...

    await EventPublisher.PublishAsync(
        new NewUserEvent { Id = "1", Name = "张三" },
        tran.ToTransactionContext()  // IDbTransaction 扩展方法
    );

    await tran.CommitAsync();
}
catch
{
    await tran.RollbackAsync();
}
⚠️ **GitHub.com Fallback** ⚠️