A state machine for .NET, licensed under LGPL.
I would love to know if you use NState in your application. I can be contacted on ben@bj.ma.
Example:
//arrangevartransitions=newIStateTransition<BugState,TransitionActionStatus>[]{newBugTransition.Open(),newBugTransition.Assign(newBugTransitionAction.Assign()),newBugTransition.Defer(newBugTransitionAction.Defer()),newBugTransition.Resolve(newBugTransitionAction.Resolve()),newBugTransition.Close(newBugTransitionAction.Close()),};varmyStateMachine=newStateMachine<BugState,TransitionActionStatus>("example",transitions,initialState:newBugState.Open());//actvarbug=newBug("my bug name",myStateMachine);//Bug type inherits from Stateful base type//assertAssert.That(bug.CurrentState,Is.TypeOf<BugState.Open>());//true //actbug.Assign("example@example.com",outtransitionActionStatus);//triggers a transition of the state machine//assertAssert.That(bug.CurrentState,Is.TypeOf<BugState.Assigned>());//trueAssert.That(transitionActionStatus,Is.EqualTo(TransitionActionStatus.Success));//trueAssert.That(bug.AssigneeEmail,Is.EqualTo("example@example.com"));//true//actvarjson=myStateMachine.ToJson();varmyDeserializedStateMachine=newStateMachine<BugState,TransitionActionStatus>("example",_transitions,initialState:newBugState.Open());myDeserializedStateMachine.InitializeFromJson(json);//assertAssert.That(myDeserializedStateMachine.CurrentState,Is.TypeOf<BugState.Assigned>());//true- easy construction of trees of interdependent or orthogonal state machines
- supports making domain objects stateful
- trivial state machine tree persistence and retrieval to/from JSON
- transition conditions, exit and entry actions
- transition actions are fully-fledged types enabling use of dependency injection to maintain clean, testable code even for complex behavior
- initial and final state specification
0. Get it
nuget install nstate1. Create some states
publicabstractclassBugState:State{publicclassAssigned:BugState{}publicclassClosed:BugState{}publicclassDeferred:BugState{}publicclassOpen:BugState{}publicclassResolved:BugState{}}2. Define your stateful type
publicclassBug:Stateful<BugState,TransitionActionStatus>{publicBug(stringtitle,IStateMachine<BugState,TransitionActionStatus>stateMachine):base(stateMachine){Title=title;}publicstringTitle{get;set;}publicstringAssigneeEmail{get;set;}publicstringClosedByName{get;set;}publicBugOpen(outTransitionActionStatustransitionActionStatus){returnTriggerTransition(this,newBugState.Open(),outtransitionActionStatus);}publicBugAssign(stringassigneeEmail,outTransitionActionStatustransitionActionStatus){dynamicdto=newExpandoObject();dto.AssigneeEmail=assigneeEmail;returnTriggerTransition(this,newBugState.Assigned(),outtransitionActionStatus,dto);}publicvoidDefer(outTransitionActionStatustransitionActionStatus){returnTriggerTransition(this,newBugState.Deferred(),outtransitionActionStatus);}publicvoidResolve(outTransitionActionStatustransitionActionStatus){returnTriggerTransition(this,newBugState.Resolved(),outtransitionActionStatus);}publicBugClose(stringclosedByName,outTransitionActionStatustransitionActionStatus){dynamicdto=newExpandoObject();dto.ClosedByName=closedByName;returnTriggerTransition(this,newBugState.Closed(),outtransitionActionStatus,dto);}}3. Define your transitions
publicpartialclassBugTransition{publicclassResolve:StateTransition<BugState,TransitionActionStatus>{publicResolve(BugTransitionAction.ResolvetransitionAction):base(transitionAction:transitionAction){}publicoverrideBugState[]StartStates{get{returnnew[]{newBugState.Assigned(),};}}publicoverrideBugState[]EndStates{get{returnnew[]{newBugState.Resolved(),};}}}publicclassAssign:StateTransition<BugState,TransitionActionStatus>{//...}//etc.}4. Define any transition actions
publicpartialclassBugTransitionAction{publicclassAssign:TransitionAction<BugState,TransitionActionStatus>{publicoverrideTransitionActionStatusRun(BugStatetargetState,IStateMachine<BugState,TransitionActionStatus>stateMachine,dynamicstatefulObject,dynamicdto=null){if(dto==null){thrownewArgumentNullException("dto");}if(dto.AssigneeEmail==null){thrownewException("AssigneeEmail not supplied.");}statefulObject.AssigneeEmail=dto.AssigneeEmail;returnTransitionActionStatus.Success;}}//etc.}5. Instantiate your state machine
//...vartransitions=newIStateTransition<BugState,TransitionActionStatus>[]{newBugTransition.Open(),newBugTransition.Assign(newBugTransitionAction.Assign()),newBugTransition.Defer(newBugTransitionAction.Defer()),newBugTransition.Resolve(newBugTransitionAction.Resolve()),newBugTransition.Close(newBugTransitionAction.Close()),};_stateMachine=newStateMachine<BugState,TransitionActionStatus>("example",transitions,initialState:newBugState.Open());//...6. Work with your stateful object
//arrangevarbug=newBug("my bug name",_stateMachine);//actbug.Assign("example@example.com",outtransitionActionStatus);//assertAssert.That(bug.CurrentState,Is.TypeOf(BugState.Assigned));//trueAssert.That(transitionActionStatus,Is.EqualTo(TransitionActionStatus.Success));//trueAssert.That(bug.AssigneeEmail,Is.EqualTo("example@example.com"));//true7. Persist and restore your state machine
//...varjson=_myStateMachine.ToJson();//send json to Couch or somewhere...//later...varmyStateMachine=newStateMachine<BugState,TransitionActionStatus>("example",_transitions,initialState:newBugState.Open());myStateMachine.InitializeFromJson(json);//continue where you left off...- Run
/build/build.bat - Type in the desired option
- Hit return
This software is released under the GNU Lesser GPL. It is Copyright 2013, Ben Aston. I may be contacted at ben@bj.ma.
Pull requests including bug fixes, new features and improved test coverage are welcomed. Please do your best, where possible, to follow the style of code found in the existing codebase.
