I have this simple process for my order process spanning multiple microservices. Created > validated> Checkout session created > payed > collected. The result can fail at any step resulting in the "Cancelled" state. I currently have the following as my state machine:
{
public Event<IOrderCreatedEvent> StartOrderProcess { get; private set; }
public Event<IOrderValidatedEvent> OrderValidated { get; private set; }
public Event<IOrderCheckoutSessionCreatedEvent> CheckoutSessionCreatedEvent { get; private set; }
public Event<IOrderPayedEvent> OrderPayed { get; private set; }
public Event<IOrderCollectedEvent> OrderCollected { get; private set; }
public Event<IOrderCancelledEvent> OrderCancelled { get; private set; }
public State Validated { get; private set; }
public State CheckoutSessionCreated { get; private set; }
public State Payed { get; private set; }
public State Collected { get; private set; }
public State Cancelled { get; private set; }
//Start => Validated => Session created => Payed => Collected
public OrderStateMachine()
{
InstanceState(s => s.CurrentState);
Event(() => StartOrderProcess, x => x.CorrelateById(m => m.Message.OrderId));
Event(() => OrderValidated, x => x.CorrelateById(m => m.Message.OrderId));
Event(() => CheckoutSessionCreatedEvent, x => x.CorrelateById(m => m.Message.OrderId));
Event(() => OrderPayed, x => x.CorrelateById(m => m.Message.OrderId));
Event(() => OrderCollected, x => x.CorrelateById(m => m.Message.OrderId));
Event(() => OrderCancelled, x => x.CorrelateById(m => m.Message.OrderId));
//Start
Initially(
When(StartOrderProcess)
.Then(context =>
{
context.Instance.OrderId = context.Data.OrderId;
context.Instance.PackagesId = context.Data.PackagesIds;
})
.Publish(context => new OrderValidatedEvent(context.Instance))
.TransitionTo(Validated)
);
//Validated => payment session created
During(Validated, When(
CheckoutSessionCreatedEvent).Then(context =>
{
context.Instance.OrderId = context.Data.OrderId;
context.Instance.PackagesId = context.Data.PackagesIds;
context.Instance.CheckoutSessionId = context.Data.SessionId;
})
.Publish(context => new OrderCheckoutSessionCreatedEvent(context.Instance))
.TransitionTo(CheckoutSessionCreated)
);
//Session created => payed
During(CheckoutSessionCreated, When(
OrderPayed).Then(context =>
{
context.Instance.OrderId = context.Data.OrderId;
context.Instance.PackagesId = context.Data.PackagesIds;
context.Instance.PaymentMethod = context.Data.PaymentMethod;
})
.Publish(context => new OrderPayedEvent(context.Instance))
.TransitionTo(Payed)
);
//payed => Collected
During(Payed, When(
OrderCollected).Then(context =>
{
context.Instance.OrderId = context.Data.OrderId;
context.Instance.PackagesId = context.Data.PackagesIds;
context.Instance.OrderCollectedDateTime = DateTime.Now;
})
.Publish(context => new OrderCollectedEvent(context.Instance))
.TransitionTo(Collected)
);
//Cancel order bij cancelled event
DuringAny(
When(OrderCancelled)
.Then(context =>
{
context.Instance.OrderCancelDateTime = DateTime.Now;
context.Instance.OrderId = context.Data.OrderId;
})
.Finalize());
SetCompletedWhenFinalized();
}
}
And this is the content of "OrderStateData"
public class OrderStateData : SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public string CurrentState { get; set; }
public DateTime? OrderCreationDateTime { get; set; }
public DateTime? OrderCancelDateTime { get; set; }
public DateTime? OrderCollectedDateTime { get; set; }
public Guid OrderId { get; set; }
public List<Guid> PackagesId { get; set; }
public string CheckoutSessionId { get; set; }
public string PaymentMethod { get; set; }
}
However when I try to use this I get the following error
R-FAULT rabbitmq://localhost/Constants.OrderBus1 5f750000-61e0-00d8-0485-08d8c20620a5 MessagingContracts.Orders.IOrderValidatedEvent FS_Saga.OrderStateMachine.OrderStateData(00:00:00.1109663)
Automatonymous.NotAcceptedStateMachineException: FS_Saga.OrderStateMachine.OrderStateData(654b5d93-cb5a-4f58-a06c-3a25ac56532d) Saga exception on receipt of MessagingContracts.Orders.IOrderValidatedEvent: Not accepted in state Validated
---> Automatonymous.UnhandledEventException: The OrderValidated event is not handled during the Validated state for the OrderStateMachine state machine
at Automatonymous.AutomatonymousStateMachine`1.DefaultUnhandledEventCallback(UnhandledEventContext`1 context)
at Automatonymous.AutomatonymousStateMachine`1.UnhandledEvent(EventContext`1 context, State state)
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)
--- End of inner exception stack trace ---
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)
I have a strong feeling my state machine is absolute garbage, but I cant find anything on how to set it up properly.
It looks like a OrderValidated
event is occurring while the state is Validated
- which is currently unhandled (only CheckoutSessionCreatedEvent
is handled at the moment). It is important to note that even though publishing the OrderValidated
event is technically occurring prior to the transition to Validated
, the consumption of the event is occurring after the transition to Validated
state (we're dealing with message brokers here).
Basically, your behavior logic is expecting a When(OrderValidated)
within During(Validated)
. Eg,
//Validated => payment session created
During(Validated, When(
CheckoutSessionCreatedEvent).Then(context =>
{
context.Instance.OrderId = context.Data.OrderId;
context.Instance.PackagesId = context.Data.PackagesIds;
context.Instance.CheckoutSessionId = context.Data.SessionId;
})
.Publish(context => new OrderCheckoutSessionCreatedEvent(context.Instance))
.TransitionTo(CheckoutSessionCreated),
When(OrderValidated).Then({ /* Do some stuff */ });
Chris is right in his comment though, it looks like you should remove the OrderValidated
event if your not going to be used for anything.
If you're half-way through something you could always just temporarily ignore the event using Ignore
. Eg,
//Validated => payment session created
During(Validated, When(
CheckoutSessionCreatedEvent).Then(context =>
{
context.Instance.OrderId = context.Data.OrderId;
context.Instance.PackagesId = context.Data.PackagesIds;
context.Instance.CheckoutSessionId = context.Data.SessionId;
})
.Publish(context => new OrderCheckoutSessionCreatedEvent(context.Instance))
.TransitionTo(CheckoutSessionCreated),
Ignore(OrderValidatedEvent));
Hopefully this makes sense. Correct me if I'm wrong @Chris Patterson.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.