Statemachine, что переходы в целевое состояние и запускает переходы и состояния между?
Я недавно использовал Stateless State Machine. Я могу определить правила для переходов и т.д., как это:
stateMachine.Configure(State.Unknown)
.Permit(Trigger.StartApplication, State.Initialized)
.OnEntry(this.DoBeforeTransition)
.OnExit(this.DoAfterTransition);
stateMachine.Configure(State.Initialized)
.Permit(Trigger.CheckSomething, State.SomethingChecked)
.OnEntry(this.DoBeforeTransition)
.OnExit(this.DoAfterTransition);
и тогда вы сможете запустить триггер, чтобы изменить состояние. Однако вам нужно знать текущее состояние и какое будет следующее состояние, если вы хотите перейти в определенное состояние. Таким образом, "клиент" машины состояний должен знать, как достичь определенного состояния, если не определен прямой переход. Есть ли возможность вызвать что-то вроде "goto", и машина запускает все необходимые триггеры самостоятельно?
1 ответ
Вы можете сделать это, если есть только ОДИН "Разрешение" на штат. Если у вас есть более одного "Разрешения", вы не можете автоматически перемещаться по рабочему процессу (должна быть какая-то причина, по которой вы должны выбрать одно Разрешение / Триггер над другим). Когда я говорю, что вы не можете, это не технически, это практически.
Ниже приведен пример автоматического перемещения по рабочему процессу.
using Stateless;
using System;
using System.Runtime.CompilerServices;
namespace MyExample.BAL.WorkFlows
{
public class TelephoneCallWorkFlow
{
private static volatile StateMachine<TelephoneCallStateEnum, TelephoneCallTriggerEnum> SingletonInstance;
public StateMachine<TelephoneCallStateEnum, TelephoneCallTriggerEnum> Instance
{
[MethodImpl(MethodImplOptions.Synchronized)]
get
{
if (SingletonInstance == null)
{
SingletonInstance = new StateMachine<TelephoneCallStateEnum, TelephoneCallTriggerEnum>(TelephoneCallStateEnum.OffHook);
SingletonInstance.Configure(TelephoneCallStateEnum.OffHook)
.Permit(TelephoneCallTriggerEnum.CallDialed, TelephoneCallStateEnum.Ringing);
SingletonInstance.Configure(TelephoneCallStateEnum.Ringing)
//removing so there is only one valid path workflow//.Permit(TelephoneCallTriggerEnum.HungUp, TelephoneCallStateEnum.OffHook)
.Permit(TelephoneCallTriggerEnum.CallConnected, TelephoneCallStateEnum.Connected);
SingletonInstance.Configure(TelephoneCallStateEnum.Connected)
//.OnEntry(t => StartCallTimer())
//.OnExit(t => StopCallTimer())
//removing so there is only one valid path workflow//.Permit(TelephoneCallTriggerEnum.LeftMessage, TelephoneCallStateEnum.OffHook)
//removing so there is only one valid path workflow//.Permit(TelephoneCallTriggerEnum.HungUp, TelephoneCallStateEnum.OffHook)
.Permit(TelephoneCallTriggerEnum.PlacedOnHold, TelephoneCallStateEnum.OnHold)
;
SingletonInstance.Configure(TelephoneCallStateEnum.OnHold)
//removing so there is only one valid path workflow//.SubstateOf(TelephoneCallStateEnum.Connected)
//removing so there is only one valid path workflow//.Permit(TelephoneCallTriggerEnum.TakenOffHold, TelephoneCallStateEnum.Connected)
//removing so there is only one valid path workflow//.Permit(TelephoneCallTriggerEnum.HungUp, TelephoneCallStateEnum.OffHook)
.Permit(TelephoneCallTriggerEnum.PhoneHurledAgainstWall, TelephoneCallStateEnum.PhoneDestroyed)
;
}
return SingletonInstance;
}
}
public void Fire(TelephoneCallTriggerEnum trigger)
{
Console.WriteLine("............[Firing:] {0}", trigger);
this.Instance.Fire(trigger);
}
}
}
public enum TelephoneCallStateEnum
{
OffHook,
Ringing,
Connected,
OnHold,
PhoneDestroyed
}
public enum TelephoneCallTriggerEnum
{
CallDialed,
HungUp,
CallConnected,
LeftMessage,
PlacedOnHold,
TakenOffHold,
PhoneHurledAgainstWall
}
и теперь трюк с автоматическим перемещением.
TelephoneCallWorkFlow tcwf1 = new TelephoneCallWorkFlow();
IEnumerable<TelephoneCallTriggerEnum> myPermittedTriggers = tcwf1.Instance.PermittedTriggers;
while (null != myPermittedTriggers && myPermittedTriggers.Count() > 0)
{
if (myPermittedTriggers.Count() > 1)
{
throw new ArgumentOutOfRangeException("You cannot auto-move the workflow when there's more than one trigger");
}
TelephoneCallTriggerEnum nextTrigger = myPermittedTriggers.FirstOrDefault();
Console.WriteLine("About to call the 'next' trigger: --> {0}", nextTrigger);
tcwf1.Fire(nextTrigger);
Console.WriteLine("CurrentState: --> {0}", tcwf1.Instance.State);
myPermittedTriggers = tcwf1.Instance.PermittedTriggers;
}
По сути, вы получаете PermittedTriggers и получаете первый (и для автоматического перемещения должен быть только один разрешенный триггер на состояние)..... и затем вызываете этот триггер.
Опять же, практически (не технически) вы бы делали это только если было одно разрешение / триггер на штат. Поэтому, почему у меня есть исключение, если их больше 1. Вы могли бы "получить первое", если их было больше 1, это просто не имело бы никакого смысла.