MassTransit Saga中注入Scoped服务的正确实现方式有哪些?
首先明确:手动创建作用域的方式不是唯一方案,MassTransit 7.x本身提供了更优雅的原生实现,不需要重复编写作用域创建代码,同时能保证作用域自动释放。
推荐实现方案
方案1:直接从消费上下文获取服务
MassTransit在处理每个Saga关联的消息时,会自动创建独立的依赖注入作用域,消息消费完成后作用域会自动释放。你可以直接通过ConsumeContext的扩展方法获取所需的Scoped服务,无需手动注入IServiceProvider创建作用域。
示例代码:
public async Task Consume(ConsumeContext<MySagaTriggerEvent> context) { var myService1 = context.GetRequiredService<IMyService1>(); var myService2 = context.GetRequiredService<IMyService2>(); // 执行业务逻辑 }
方案2:将服务注入Saga状态机的Activity
如果你使用的是MassTransit状态机Saga(MassTransit State Machine),可以直接把所需的Scoped服务注入到自定义Activity的构造函数中,MassTransit实例化Activity时会自动从当前消息的作用域中解析服务,你无需关心作用域的创建和释放逻辑。
示例代码:
public class MyCustomActivity : IStateMachineActivity<MySagaState, MyTriggerEvent> { private readonly IMyService1 _myService1; private readonly IMyService2 _myService2; // 直接注入Scoped服务即可 public MyCustomActivity(IMyService1 myService1, IMyService2 myService2) { _myService1 = myService1; _myService2 = myService2; } public async Task Execute(BehaviorContext<MySagaState, MyTriggerEvent> context, IBehavior<MySagaState, MyTriggerEvent> next) { // 直接使用注入的服务 await _myService1.ProcessData(context.Data); await next.Execute(context); } // 其余接口方法实现省略 }
注意事项
- 上述两种方案都不需要额外配置MassTransit,只需将
IMyService1、IMyService2按普通Scoped服务的方式注册到ASP.NET Core DI容器中即可。 - 每个Saga步骤对应的消息消费是独立的,所以不同步骤获取到的Scoped服务实例是相互独立的,符合Scoped服务的生命周期设计,不会出现线程安全、资源泄漏等问题。
- 禁止将服务实例存储到SagaState中,SagaState只能存储业务状态数据,存储服务实例会导致序列化失败、内存泄漏、服务实例失效等问题。
- 如果确实有跨多个Saga步骤共享数据的需求,只需要将业务参数存储到SagaState中,每个步骤执行时从当前作用域获取服务后传入参数即可。
内容的提问来源于stack exchange,提问作者Volodymyr V.
相关产品推荐
相关产品推荐

