Skip to content

4. Changing State with Input

Ovan Crone edited this page Apr 11, 2018 · 9 revisions

We will be interacting with Machines to change states when we provide input. To do so effectively, let's recap the code so far.

var schematic = REstateHost.Agent
    .CreateSchematic<string, string>("LoggerMachine")
    .WithState("Created", state => state
        .AsInitialState())
    .WithState("Ready", state => state
        .WithTransitionFrom("Created", "log")
        .WithReentrance("log")
        .WithAction("log info", onEntry => onEntry
            .DescribedAs("Logs the transition as a message.")
            .WithSetting(
                key: "messageFormat", 
                value: "{schematicName}({machineId}) entered {state} on {input}. " +
                       "Message: {payload}")))
    .Build();

var stateEngine = REstateHost.Agent
    .GetStateEngine<string, string>();

var machine = await stateEngine.CreateMachineAsync(schematic);

Sending Input to change State

Now that we can look back on the Schematic, we can see that when we created it, it should be in its initial state, "Created". "Created" has a single transition defined from "Created" when it receives the input "log", which we can send now.

var result = await machine.SendAsync("log");

The result of the call will be a Status<TState> which for our Machine is Status<string> since our state type is string. Inspecting the result shows us a few properties:

  • The MachineId of the Machine with which we interacted.
  • The state the Machine is in after processing the input.
  • A timestamp of when the transition occurred.
  • A commit number, a unique incrementing long (64-bit integer) that uniquely identifies a transition in a Machine.

Working with Payloads

Controlling state with input is helpful, but doesn't really allow for anything other than simple state tracking. However, payloads can be sent along with input that can be used for more complicated logic in a connector, such as putting a message on a queue or the body of an HTTP POST call. Sending input is just adding another parameter

var stateChangeResult4 = await machine.SendAsync("log", "This is my payload.");

If you have any logger in your application, we should have seen a log statement similar to:

"LoggerMachine({machineId}) entered Ready on log. Message: This is my payload."

Using CommitNumber to Conditionally Transition

Occasionally, sending input and transitioning may only be correct if the state hasn't changed since a previous commit tag even if it is in the same state. An example of this is a re-entrant state or loop in the Schematic for retries, delays, etc. Now that our Machine is in the "Ready" state we can show this behavior by sending the same input, and then sending input with our original commit number. This should throw a StateConflictException, so don't be surprised!

var stateChangeResult2 = await machine.SendAsync("log");

// Now we send the ORIGINAL commit number from our first transition.
var stateChangeResult3 = await machine.SendAsync("log", result.CommitNumber);

A special note on payloads

If your payload happens to be a Guid this will confuse C#, so make sure to use the generic parameter form explicitly, otherwise the Guid will be interpreted as a commit tag!

var stateChangeResult5 = await machine.SendAsync<Guid>("log", Guid.NewGuid());