I am implementing a Saga for a distributed system. So far I haven't got any problem using Masstransit documentation to implement a Saga without any meaningful behavior (just an orchestrator with a couple of microservices exchanging messages).
The next step was adding some behavior between state changes of the Automatonymous state machine. I have a service with all the required behavior so I want to create customs activities that use this service using dependency injection.
The problem is that the activities do not resolve well and the following error is thrown:
Automatonymous.EventExecutionException: The ArchivosEnviados<ArchivosEnviadosAAlfresco> (Event) execution faulted
---> System.MissingMethodException: No parameterless constructor defined for type 'Confuturo.Microservicios.BackendAppSucursalVirtual.Sagas.Activities.EnviarCorreoActivity'.
at System.RuntimeType.CreateInstanceDefaultCtorSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean fillCache)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, Boolean wrapExceptions)
at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
at System.Activator.CreateInstance(Type type)
at Automatonymous.DefaultConstructorStateMachineActivityFactory.GetActivity[TActivity,TInstance](BehaviorContext`1 context)
at Automatonymous.Activities.ContainerFactoryActivity`2.Automatonymous.Activity<TInstance>.Execute(BehaviorContext`1 context, Behavior`1 next)
at Automatonymous.Activities.SlimActivity`2.Automatonymous.Activity<TInstance,TData>.Execute(BehaviorContext`2 context, Behavior`2 behavior)
at Automatonymous.Activities.DataConverterActivity`2.Automatonymous.Activity<TInstance>.Execute[T](BehaviorContext`2 context, Behavior`2 next)
at Automatonymous.Behaviors.ActivityBehavior`1.Automatonymous.Behavior<TInstance>.Execute[T](BehaviorContext`2 context)
--- End of inner exception stack trace ---
at Automatonymous.Behaviors.ExceptionBehavior`2.Automatonymous.Behavior<TInstance,TData>.Faulted[TException](BehaviorExceptionContext`3 context)
at Automatonymous.Activities.SlimActivity`2.Automatonymous.Activity<TInstance,TData>.Faulted[TException](BehaviorExceptionContext`3 context, Behavior`2 next)
at Automatonymous.Activities.DataConverterActivity`2.Automatonymous.Activity<TInstance>.Faulted[T,TException](BehaviorExceptionContext`3 context, Behavior`2 next)
at Automatonymous.Behaviors.LastBehavior`1.Automatonymous.Behavior<TInstance>.Faulted[T,TException](BehaviorExceptionContext`3 context)
at Automatonymous.Behaviors.DataBehavior`2.Automatonymous.Behavior<TInstance,TData>.Faulted[TException](BehaviorExceptionContext`3 context)
at Automatonymous.Behaviors.ExceptionTypeCache.CachedConfigurator`1.Automatonymous.Behaviors.ExceptionTypeCache.CachedConfigurator.Faulted[TInstance,TData](Behavior`2 behavior, BehaviorContext`2 context, Exception exception)
at Automatonymous.Behaviors.ExceptionTypeCache.Faulted[TInstance,TData](Behavior`2 behavior, BehaviorContext`2 context, Exception exception)
at Automatonymous.Behaviors.ActivityBehavior`1.Automatonymous.Behavior<TInstance>.Execute[T](BehaviorContext`2 context)
at Automatonymous.States.StateMachineState`1.Automatonymous.State<TInstance>.Raise[T](EventContext`2 context)
at Automatonymous.States.StateMachineState`1.Automatonymous.State<TInstance>.Raise[T](EventContext`2 context)
at Automatonymous.AutomatonymousStateMachine`1.Automatonymous.StateMachine<TInstance>.RaiseEvent[T](EventContext`2 context)
at Automatonymous.Pipeline.StateMachineSagaMessageFilter`2.Send(SagaConsumeContext`2 context, IPipe`1 next)
at Automatonymous.Pipeline.StateMachineSagaMessageFilter`2.Send(SagaConsumeContext`2 context, IPipe`1 next)
at MassTransit.Saga.SendSagaPipe`2.Send(SagaRepositoryContext`2 context)
at MassTransit.Saga.SendSagaPipe`2.Send(SagaRepositoryContext`2 context)
at MassTransit.Saga.InMemoryRepository.InMemorySagaRepositoryContextFactory`1.Send[T](ConsumeContext`1 context, IPipe`1 next)
at MassTransit.Saga.Pipeline.Filters.CorrelatedSagaFilter`2.GreenPipes.IFilter<MassTransit.ConsumeContext<TMessage>>.Send(ConsumeContext`1 context, IPipe`1 next)
The documentation does not explicitly states how we should include custom activities using the default DI system of .NET Core. I also looked up for this information in Stackoverflow, the Github repo and Google Group.
In my Startup class I configure Masstransit as is stated in the official docs:
services.AddMassTransit(c =>
{
var machineGenerica = new SolicitudGenericStateMachine();
var repositoryGenerico = new InMemorySagaRepository<SolicitudGenericInstance>();
c.AddConsumer<MensajeDocumentoFirmadoConsumer>();
c.AddBus(context => Bus.Factory.CreateUsingActiveMq(cfg =>
{
cfg.ReceiveEndpoint("solicitud-generica-saga", e =>
{
e.StateMachineSaga(machineGenerica, repositoryGenerico);
});
cfg.UseHealthCheck(context);
cfg.Host(activeMqOptions.Host, activeMqOptions.Port, hostConfigure =>
{
hostConfigure.Username(activeMqOptions.Username);
hostConfigure.Password(activeMqOptions.Password);
});
cfg.ReceiveEndpoint(activeMqOptions.EndpointDocumentoFirmado, ep =>
{
ep.PrefetchCount = 1;
ep.Consumer<MensajeDocumentoFirmadoConsumer>(context);
});
}));
});
services.AddMassTransitHostedService();
services.AddScoped<EnviarCorreoActivity>();
If I understand correctly I have to use somehow the IStateMachineActivityFactory but I am really lost on how to proceed. Any help would be greatly appreciated.
You should register your state machine in the container using:
c.AddSagaStateMachine< SolicitudGenericStateMachine, SolicitudGenericInstance >()
.InMemoryRepository();
Then, configure the saga using:
cfg.ReceiveEndpoint("solicitud-generica-saga", e =>
{
e.ConfigureSaga<SolicitudGenericInstance>(context);
});
The activity you have registered as scoped, is correct.