Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I correlate events in a masstransit state machine without using a Guid?

I have defined the following state machine in Masstransit:

public class OrderStateMachine : MassTransitStateMachine<OrderState>
{
    public OrderStateMachine()
    {
        InstanceState(x => x.Status);

        Event(() => OrderCreated, x => x.CorrelateBy(order => order.OrderCode, ctx => ctx.Message.OrderCode).SelectId(ctx => NewId.NextGuid()));

        //How should I select an id for these events?
        Event(() => OrderProvisioned, x => x.CorrelateBy(order => order.OrderCode, ctx => ctx.Message.OrderCode));
        Event(() => OrderInvoiced, x => x.CorrelateBy(order => order.OrderCode, ctx => ctx.Message.OrderCode));

        State(() => Created);
        State(() => Finished);

        CompositeEvent(() => OrderFinished, order => order.CompositeStatus, OrderProvisioned, OrderInvoiced);

        Initially(
            When(OrderCreated)
                .Then(context => Console.WriteLine("Order created"))
                .TransitionTo(Created));

        During(Created,
            When(OrderFinished)
                .Then(context => Console.WriteLine("Order finished"))
                .TransitionTo(Finished)
                .Finalize());

    }

    public State Created { get; set; }
    public State Finished { get; set; }

    public Event<OrderCreated> OrderCreated { get; set; }
    public Event<OrderProvisioned> OrderProvisioned { get; set; }
    public Event<OrderInvoiced> OrderInvoiced { get; set; }
    public Event OrderFinished { get; set; }

}

public class OrderState : SagaStateMachineInstance
{
    public Guid CorrelationId { get; set; }

    public string OrderCode { get; set; }
    public string Status { get; set; }
    public CompositeEventStatus CompositeStatus { get; set; }

}

public class OrderCreated
{
    public string OrderCode { get; set; }

    public OrderCreated(string orderCode)
    {
        OrderCode = orderCode;
    }
}

public class OrderInvoiced
{
    public string OrderCode { get; set; }

    public OrderInvoiced(string orderCode)
    {
        OrderCode = orderCode;
    }

}

public class OrderProvisioned
{
    public string OrderCode { get; set; }

    public OrderProvisioned(string orderCode)
    {
        OrderCode = orderCode;
    }
}

How can I correlate the OrderProvisoned and the OrderInvoiced event to the same OrderState instance as the initial OrderCreated event without sending Guids in my events and only use the ordercode property to correlate them? If I run this example, I never get the OrderFinished event if both OrderProvisioned and OrderInvoiced are sent, but if I add Guids to the events and correlate them based on that Guid it is executed correctly.

like image 537
Sven Schelfaut Avatar asked Feb 05 '16 14:02

Sven Schelfaut


People also ask

What is Automatonymous?

Automatonymous is a state machine library for . NET and provides a C# syntax to define a state machine, including states, events, and behaviors. MassTransit includes Automatonymous, and adds instance storage, event correlation, message binding, request and response support, and scheduling.

What is MassTransit saga?

A saga is a long-lived transaction managed by a coordinator. Sagas are initiated by an event, sagas orchestrate events, and sagas maintain the state of the overall transaction. Sagas are designed to manage the complexity of a distributed transaction without locking and immediate consistency.

What is saga state?

SAGA (State Administered General Assistance) is administered by the Connecticut Department of Social Services (DSS).


2 Answers

I solved it, apparently you must explicitly set the custom correlationid on the statemachine instance, I would expect that masstransit would do that for me. So this is the adapted code for the initial state:

            Initially(
            When(OrderCreated)
                .Then(context => context.Instance.OrderCode = context.Data.OrderCode)
                .Then(context => Console.WriteLine("Order created"))
                .TransitionTo(Created));

Also using a saga factory works, but I would think that you could drop the selectid logic as the saga factory overrules this, but than I get an exception.

        Event(() => OrderCreated,
            x =>
            {
                x.CorrelateBy(order => order.OrderCode, ctx => ctx.Message.OrderCode);
                x.InsertOnInitial = true;
                x.SetSagaFactory(context => new OrderState
                {
                    CorrelationId = NewId.NextGuid(),
                    OrderCode = context.Message.OrderCode
                });
                x.SelectId(context => NewId.NextGuid());
            });
like image 52
Sven Schelfaut Avatar answered Oct 29 '22 15:10

Sven Schelfaut


Is there a broken unit test that would be usable to see if this behavior is not working? From the edited code above, that seems to be how I would expect it to be correlated - using the OrderCode. That's how the shopping cart example works as well.

The SelectId() function is only needed for an event that initiates the state machine, since that is where the CorrelationId is assigned. If it isn't used by the other events, there is no need to "select" it since the Query (the orderCode) would do the correlation.

That's how the shopping cart example works in the sample: https://github.com/MassTransit/Sample-ShoppingWeb/blob/master/src/CartTracking/ShoppingCartStateMachine.cs#L15

In your example above:

Event(() => OrderCreated, x => x.CorrelateBy(order => order.OrderCode, ctx => ctx.Message.OrderCode).SelectId(ctx => NewId.NextGuid()));

Event(() => OrderProvisioned, x => x.CorrelateBy(order => order.OrderCode, ctx => ctx.Message.OrderCode));
Event(() => OrderInvoiced, x => x.CorrelateBy(order => order.OrderCode, ctx => ctx.Message.OrderCode));

This is exactly how I would configure it, and it seems like you're on the right track.

like image 32
Chris Patterson Avatar answered Oct 29 '22 14:10

Chris Patterson