I am trying to make a state machine that should send an email if my action ends with an OK status OR it should repeat the action at least n-times until it ends with an OK status. If the n-times threshold is exceeded, the state machine should just do a rollback and stop.
I played around with the states and some of the method provided, but I couldn't manage to use the TransitionTo(State) method inside an if to act like a 'goto' and jump to the State ignoring every line of code below this point.
So, to put it in C# pseudocode, it looks like this (this is just a representation in case the state machine code is hard to understand, I do not use this code in my app) :
numberOfRetries = 5;
CheckActionStatus:
if (GetActionStatus() == Status.OK)
{
return SendEmailToUser();
}
else
{
numberOfRetries--;
}
if (numberOfRetries > 0)
{
goto CheckActionStatus;
}
else
{
return RollbackAction();
}
And the state machine code that I use (keep in mind that the decrement of the numberOfRetries is done within GetActionStatus())
WhenEnter(Check, binder => binder
.Then(x => x.Instance.ActionStatus = GetActionStatus())
.If(x => x.Instance.ActionStatus == Status.OK,
x => x.TransitionTo(SendEmailToUser))
.IfElse(x => x.Instance.NumberOfRetries > 0,
x => x.TransitionTo(Check),
x => x.TransitionTo(Rollback)));
WhenEnter(SendEmailToUser, binder => binder
.Then(x => Console.WriteLine("Email sent to user, everything ok!"))
.Finalize());
WhenEnter(Rollback, binder => binder
.Then(x => Console.WriteLine("Rollbacked!"))
.Finalize());
SetCompletedWhenFinalized();
But when I run the state machine, the output I get is:
Email sent to user, everything ok!
Rollbacked!
When the expected output should be just
Email sent to user, everything ok!
or just
Rollbacked!
Because I expect that if the first if has a true condition, it should transfer to the next state (SendEmailToUser in my case) and continue execution from there. Only if the condition evaluates to false, only then it should continue to the second if (the one that checks for the retries).
Am I missing something?
After Chris's answer, I changed the way I implemented my state machine in two different ways: Method 1: nested if-else
WhenEnter(Check, binder => binder
.Then(x => x.Instance.ActionStatus = GetActionStatus())
.IfElse(x => x.Instance.ActionStatus == Status.OK,
x => x.TransitionTo(SendEmailToUser),
xx => xx.IfElse(xy => xy.Instance.NumberOfRetries > 0,
x => x.TransitionTo(Check),
x => x.TransitionTo(Rollback))));
WhenEnter(SendEmailToUser, binder => binder
.Then(x => Console.WriteLine("Email sent to user, everything ok!"))
.Finalize());
WhenEnter(Rollback, binder => binder
.Then(x => Console.WriteLine("Rollbacked!"))
.Finalize());
SetCompletedWhenFinalized();
This way, the state finishes with the if-else and nothing more is executed, making sure the transition to desired state is done.
Method 2: intermediate state
WhenEnter(Check, binder => binder
.Then(x => x.Instance.ActionStatus = GetActionStatus())
.IfElse(x => x.Instance.ActionStatus == Status.OK,
x => x.TransitionTo(SendEmailToUser),
x => x.TransitionTo(RetryCheck)));
WhenEnter(RetryCheck, binder => binder
.IfElse(x => x.Instance.NumberOfRetries > 0,
x => x.TransitionTo(Check),
x => x.TransitionTo(Rollback)));
WhenEnter(SendEmailToUser, binder => binder
.Then(x => Console.WriteLine("Email sent to user, everything ok!"))
.Finalize());
WhenEnter(Rollback, binder => binder
.Then(x => Console.WriteLine("Rollbacked!"))
.Finalize());
SetCompletedWhenFinalized();
This method also works and I find it more easy to understand since you basically just add another condition to your state machine.