使用Mass Transit事务性Outbox模式时RabbitMQ关联表无数据问题
Publish操作未纳入数据库事务范围
你的Create方法直接调用dbContext.SaveChanges()提交了文档数据的事务,后续的Publish操作是独立执行的——Mass Transit的Bus Outbox要求消息发布必须和数据库变更处于同一个事务,这样消息才会被写入Outbox表,等待RabbitMQ恢复后重试发送。解决办法:
- 移除
Create方法中的dbContext.SaveChanges(),改为在处理器中统一提交事务,确保Publish和数据库变更在同一事务内:// 修改Create方法,去掉SaveChanges public new void Create(Document document) { dbContext.Set<Document>().Add(document); } // 处理器中统一管理事务 public async Task Handle(CreateCommand request, CancellationToken cancellationToken) { var personnelDoc = new NonPersonnelDocument(request.ImportationReason); documentsRepository.Create(personnelDoc); // 先执行Publish(由Outbox拦截并暂存),再提交数据库事务 await _publishendpoint.Publish(request, cancellationToken); await dbContext.SaveChangesAsync(cancellationToken); }
更规范的方式是使用Mass Transit提供的
ITransactionalBus,它会自动将消息纳入当前数据库事务流程。- 移除
DbContext中重复配置Outbox实体
在OnModelCreating里你重复调用了modelBuilder.AddOutboxStateEntity();,重复的实体映射会导致数据库表结构异常,无法正常写入Outbox数据。需要移除重复的配置行:protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(CenterMapping).Assembly); modelBuilder.AddInboxStateEntity(); modelBuilder.AddOutboxMessageEntity(); // 移除重复的AddOutboxStateEntity()调用 }Bus实例未正确初始化
你注释掉了cfg.AutoStart = true;,虽然消息写入Outbox和Bus启动无关,但Outbox的消息重发器依赖正常运行的Bus实例。建议开启AutoStart,确保Bus实例正常初始化,避免后续RabbitMQ恢复后消息无法重发:configuration.UsingRabbitMq((ctx, cfg) => { cfg.Host("rabbitmq"); cfg.AutoStart = true; // 取消注释 });自定义EventBus未使用事务性发布逻辑
如果你封装的_eventBus没有正确使用Mass Transit集成后的IPublishEndpoint,直接调用的Publish不会触发Outbox的暂存逻辑。确认注入的IPublishEndpoint是Mass Transit通过AddMassTransit配置的实例,而非自定义的非事务性实现。
内容的提问来源于stack exchange,提问作者HassanJalali

