Last active
January 19, 2022 13:19
-
-
Save josellm/95b04a3e1c45a4c0fb01a0186865a109 to your computer and use it in GitHub Desktop.
Reusing stateless state machine for different instances
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Stateless; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
namespace WorkFlow { | |
public class MyStateMachine<T, TTrigger> { | |
private readonly static object locker = new object(); | |
private readonly T _element; | |
private readonly StateMachine<StateEntity<T, TTrigger>, string> _machine; | |
private readonly static Dictionary<string, StateMachine<StateEntity<T, TTrigger>, string>> internalMachines = new Dictionary<string, StateMachine<StateEntity<T, TTrigger>, string>>(); | |
public MyStateMachine(T element, string initialState, string machineName) { | |
_element = element; | |
State = initialState; | |
lock (locker) { | |
if (!internalMachines.TryGetValue(machineName, out _machine)) { | |
//Create Stateless StateMachine | |
var state = new StateEntity<T, TTrigger>(); | |
_machine = new StateMachine<StateEntity<T, TTrigger>, string>(() => state, (stateEntity) => stateEntity.StateMachine.State = stateEntity.State); | |
//Configure states | |
ConfigMachine(machineName); | |
internalMachines.Add(machineName, _machine); | |
} | |
} | |
} | |
public T Element { get { return _element; } } | |
public string State { get; internal set; } | |
public bool Allow(string triggerName, TTrigger triggerParam) { | |
lock (locker) { | |
try { | |
SetMachineState(); | |
return _machine.CanFire(new StateMachine<StateEntity<T, TTrigger>, string>.TriggerWithParameters<TriggerEntity<T, TTrigger>>(triggerName), new TriggerEntity<T, TTrigger> { StateMachine = this, TriggerParam = triggerParam }); | |
} catch (Exception ex) { | |
Logger.Error(@"StatelessStateMachine::Allow", ex); | |
return false; | |
} | |
} | |
} | |
public bool Fire(string triggerName, TTrigger triggerParam) { | |
if (!Allow(triggerName, triggerParam)) | |
return false; | |
lock (locker) { | |
SetMachineState(); | |
_machine.Fire(new StateMachine<StateEntity<T, TTrigger>, string>.TriggerWithParameters<TriggerEntity<T, TTrigger>>(triggerName), new TriggerEntity<T, TTrigger> { StateMachine = this, TriggerParam = triggerParam }); | |
return true; | |
} | |
} | |
private void SetMachineState() { | |
_machine.State.State = State; | |
_machine.State.StateMachine = this; | |
} | |
private void ConfigMachine(string machineName) { | |
//State machine configuration example | |
_machine.Configure(new StateEntity<T, TTrigger> { StateMachine = this, State = "State1" }) | |
.OnEntry(tran => Logger.Debug(@"Executing action on entry to {1} from {0} by trigger {2}", tran.Source.State, tran.Destination.State, tran.Trigger)) | |
.PermitDynamicIf( | |
new StateMachine<StateEntity<T, TTrigger>, string>.TriggerWithParameters<TriggerEntity<T, TTrigger>>("Trigger1"), | |
trigger => { | |
//Custom stuff | |
return new StateEntity<T, TTrigger> { StateMachine = trigger.StateMachine, State = "State2" }; | |
}, | |
trigger => CustomChecks(trigger)); | |
_machine.Configure(new StateEntity<T, TTrigger> { StateMachine = this, State = "State2" }); | |
} | |
private bool CustomChecks(TriggerEntity<T, TTrigger> trigger) { | |
//I have access to current trigger.StateMachine | |
return true; | |
} | |
} | |
public class StateEntity<T, TTrigger> { | |
public string State { get; internal set; } | |
public MyStateMachine<T, TTrigger> StateMachine { get; set; } | |
public override bool Equals(object obj) { | |
var state = obj as StateEntity<T, TTrigger>; | |
return state != null && state.State == this.State; | |
} | |
public override int GetHashCode() { | |
return State != null? State.GetHashCode() : 0; | |
} | |
} | |
internal class TriggerEntity<T, TTrigger> { | |
public MyStateMachine<T, TTrigger> StateMachine { get; set; } | |
public TTrigger TriggerParam { get; set; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment