-
-
Save mahizsas/11150403 to your computer and use it in GitHub Desktop.
This file contains 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
// this script works in linqpad (https://www.linqpad.net/) | |
void Main() | |
{ | |
var day = new DaysRevisions(); | |
day.State.Dump(); | |
day.State.To(LifecycleState.Draft, "me"); | |
day.State.Dump(); | |
day.State.To(LifecycleState.Historical, "me"); | |
} | |
public class StateMachine<TEnum> where TEnum : struct, IConvertible | |
{ | |
protected Dictionary<TEnum, Func<StateMachine<TEnum>, bool>> _rules; | |
[NotMapped] | |
public TEnum State { | |
get | |
{ | |
if (string.IsNullOrEmpty(StateString)) | |
{ | |
return (TEnum)(Enum.GetValues(typeof (TEnum)).GetValue(0)); | |
} | |
return (TEnum)Enum.Parse(typeof (TEnum), StateString); | |
} | |
set | |
{ | |
StateString = value.ToString(); | |
} | |
} | |
public string StateString { get; set; } | |
public string LastTransitionBy { get; protected set; } | |
public class StateTransitionPredicateException : Exception | |
{ | |
public StateTransitionPredicateException(string message) | |
: base(message) {} | |
} | |
public class StateMachineRequiredStateException : Exception | |
{ | |
public StateMachineRequiredStateException(string message) | |
: base(message) { } | |
} | |
public StateMachine(Dictionary<TEnum, Func<StateMachine<TEnum>, bool>> rules) | |
{ | |
if (!typeof(TEnum).IsEnum) | |
{ | |
throw new ArgumentException("TEnum must be an enumerated type"); | |
} | |
_rules = rules; | |
LastTransitionBy = ""; | |
StateString = ""; | |
} | |
protected StateMachine() | |
{} | |
public StateMachine<TEnum> To(TEnum targetState, string approver) | |
{ | |
var predicate = _rules.ContainsKey(targetState) | |
? _rules[targetState] : (sm) => true; | |
if (predicate(this)) | |
{ | |
State = targetState; | |
StateString = targetState.ToString(); | |
LastTransitionBy = approver; | |
} | |
else | |
{ | |
throw new StateTransitionPredicateException(string.Format("The transition from {0} to {1} was not allowed",State, targetState)); | |
} | |
return this; | |
} | |
public bool Is(TEnum state) | |
{ | |
return State.Equals(state); | |
} | |
public void Require(TEnum required) | |
{ | |
if (!Is(required)) | |
{ | |
throw new StateMachineRequiredStateException("Expected state " + required + " but was " + State); | |
} | |
} | |
public static Func<StateMachine<TEnum>, bool> AllowTransitionFrom(params TEnum[] fromStates) | |
{ | |
return (sm) => fromStates.Contains(sm.State); | |
} | |
} | |
public class DaysRevisions | |
{ | |
public virtual StateMachine<LifecycleState> State { get; protected set; } | |
public DaysRevisions() | |
{ | |
var capturedRandomCutoff = 690063227; | |
// this is the definition of the allowed transitions | |
State = new StateMachine<LifecycleState>(new Dictionary<LifecycleState, Func<StateMachine<LifecycleState>, bool>> | |
{ | |
{LifecycleState.Draft, StateMachine<LifecycleState>.AllowTransitionFrom(LifecycleState.Unknown)}, | |
{LifecycleState.Approved, StateMachine<LifecycleState>.AllowTransitionFrom(LifecycleState.Draft)}, | |
{LifecycleState.Historical, StateMachine<LifecycleState>.AllowTransitionFrom(LifecycleState.Approved)}, | |
{LifecycleState.Unknown, (sm) => new Random().Next() > capturedRandomCutoff} | |
}); | |
} | |
} | |
public enum LifecycleState | |
{ | |
Unknown, | |
Draft, | |
Approved, | |
Historical | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment