Dependency Injection into Sagas

1,314 views
Skip to first unread message

Bartosz Ladziński

unread,
Sep 21, 2017, 12:39:25 PM9/21/17
to masstransit-discuss
I have a saga and a state machine, did some test dry runs and the configuration was okay, however, I've tried to add some Dependency Injection (SimpleInjector) into the mix and I can't seem to get it working.

public class TestSaga : SagaStateMachineInstance, IVersionedSaga
   
{
       
private readonly ITestService testService;


       
public TestSaga(
           
ITestService testService,
           
Guid correlationId)
       
{
           
this.testService = testService;
           
CorrelationId = correlationId;
       
}


       
public async Task DoSomething()
       
{
            await testService
.DoSomethingElse();
       
}


       
public Guid CorrelationId { get; set; }


       
public string CurrentState { get; set; }


       
public int Version { get; set; }


       
public long SomeId { get; set; }

   
}


public class TestSagaStateMachine :
       
MassTransitStateMachine<TestSaga>
   
{
       
public TestSagaStateMachine()
       
{
           
InstanceState(x => x.CurrentState);


           
Event(() => SomethingHappened , x => x.CorrelateBy(((saga, context) => saga.SomeId == context.Message.SomeId)).SelectId(context => NewId.NextGuid()));


           
Initially(
               
When(SomethingHappened )
                   
.Then(context => context.Instance.SomeId = context.Data.SomeId )
                   
.ThenAsync(async context =>
                   
{
                        await context
.Instance.DoSomething();
                   
})
                   
.TransitionTo(Active)
                   
.Publish(context => (ISomethingElseHappenedEvent)new SomethingElseHappenedEvent(context.Data.SomeId))
           
);


           
//more code here


           
SetCompletedWhenFinalized();
       
}


       
public State Active { get; private set; }


       
public Event<ISomethingHappenedEvent> SomethingHappened { get; private set; }
   
}


Unfortunately, in this scenario I have no control over how the TestSaga objects are created, so the registration of the service that I've made is not take into account.

Is there some way to inject a service like that? Is it possible to take control over the saga instance creation?

Chris Patterson

unread,
Sep 21, 2017, 1:23:48 PM9/21/17
to masstrans...@googlegroups.com
If you're using Autofac, there is out-of-the-box support for resolving state machine activities from the container, which can have dependencies. The actual state machine itself shouldn't have any dependencies since it's created once, and not within the message lifetime scope.

See this:

And using the registration extension:


--
You received this message because you are subscribed to the Google Groups "masstransit-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsub...@googlegroups.com.
To post to this group, send email to masstransit-discuss@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/masstransit-discuss/d906887a-5c41-48e5-a71b-d9812ddb8809%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Message has been deleted

Bartosz Ladziński

unread,
Sep 21, 2017, 1:47:25 PM9/21/17
to masstransit-discuss
Hey Chris,

Thanks for the reply. Unfortunately in the whole solution we already use SimpleInjector, so I guess I need to work it out differently?

As far as I understand the TestSagaStateMachine shouldn't have any dependencies because it's basically a singleton, however, the TestSaga which inherits from SagaStateMachineInstance is okay to have those as it's created per each SomethingHappenedEvent that arrives? Is my thinking correct here?

Alexey Zimarev

unread,
Sep 21, 2017, 3:26:33 PM9/21/17
to masstransit-discuss
State machine instance is a property bag, it should not have any dependencies. Even with out-of-the-box support for Autofac, there is no way to inject dependencies in the message lifecycle. Sagas are injected as singletons and dependencies are inejcted accordingly. What we do if we need message-scoped instances - we inject delegate factories and get new instances of dependencies for each messages, on demand.

So it is like this:

public class MySagaStateMachine : MassTransitStateMachine<MySagaInstance>
{
   
private readonly Func<IMyShortLivingDependency> _shortLivingDependencyFactory;


   
public MySagaStateMachine(Func<IMyShortLivingDependency> shortLivingDependencyFactory)
   
{
        _shortLivingDependencyFactory
= shortLivingDependencyFactory;


       
....


       
During(Whatever,
           
When(Something).
               
Then(c => shortLivingDependencyFactory().HereWeGo(c.Data))
       
....
   
}


}

Chris Patterson

unread,
Sep 22, 2017, 12:54:42 AM9/22/17
to masstrans...@googlegroups.com
Alexey,

You need to look at what I did with Autofac (and StructureMap). Should be easy enough to add support with SimpleInjector, I just didn't do it. It's using Activity factories, and it works flawlessly for message-scoped dependencies.


--
You received this message because you are subscribed to the Google Groups "masstransit-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsub...@googlegroups.com.
To post to this group, send email to masstransit-discuss@googlegroups.com.

Alexey Zimarev

unread,
Sep 22, 2017, 7:35:15 AM9/22/17
to masstransit-discuss
Chris,

Certainly, but it was not me asking :) I have done my own Autofac module fo sagas about six months before you done yours and they are very similar, so I think I know how this works :D

Regards,
Alexey

Bartosz Ladziński

unread,
Sep 22, 2017, 11:21:42 AM9/22/17
to masstransit-discuss
Hey Chris,

Is it possible to keep using the MongoDbSagaRepository and just use a specific implementation of IStateMachineActivityFactory? I'd like to still use the MongoDb as the persistence layer, however, it seems that I'd have to rewrite the MongoDbSagaRepository with the additional support for the SimpleInjector. Is that right? Or am I missing something (probably I am).

Chris Patterson

unread,
Sep 22, 2017, 12:03:59 PM9/22/17
to masstrans...@googlegroups.com
You stack them on top of each other, inject one into the other.

So MongoDB -> StateMachineSagaRepository -> your receive endpoint.

So you'd register your MongoDbSagaRepository into the container (as ISagaRepository<T>), then you'd pass it to the StateMachineSagaRepository as you configure the receive endpoint. Similar to:


Which is what the extension method calls.


On Fri, Sep 22, 2017 at 10:21 AM, Bartosz Ladziński <bartosz....@gmail.com> wrote:
Hey Chris,

Is it possible to keep using the MongoDbSagaRepository and just use a specific implementation of IStateMachineActivityFactory? I'd like to still use the MongoDb as the persistence layer, however, it seems that I'd have to rewrite the MongoDbSagaRepository with the additional support for the SimpleInjector. Is that right? Or am I missing something (probably I am).

--
You received this message because you are subscribed to the Google Groups "masstransit-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to masstransit-discuss+unsub...@googlegroups.com.
To post to this group, send email to masstransit-discuss@googlegroups.com.

Bartosz Ladziński

unread,
Sep 26, 2017, 8:26:50 AM9/26/17
to masstransit-discuss
Cool, thanks! Managed to get it working this way :)
Reply all
Reply to author
Forward
0 new messages