如何从bbv.Common.StateMachine(现在的Appccelerate.StateMachine)类中获取当前状态?

bbv.Common.StateMachine类是我见过的最好的状态机代码。 但它只缺少一件事:获得当前状态。

这是一个订单跟踪系统:

 fsm = new ActiveStateMachine(); fsm.In(States.OrderCreated) .On(Events.Submitted) .Goto(States.WaitingForApproval); fsm.In(States.WaitingForApproval) .On(Events.Reject) .Goto(States.Rejected); fsm.In(States.WaitingForApproval) .On(Events.Approve) .Goto(States.BeingProcessed); fsm.In(States.BeingProcessed) .On(Events.ProcessFinished) .Goto(States.SentByMail); fsm.In(States.SentByMail) .On(Events.Deliver) .Goto(States.Delivered); fsm.Initialize(States.OrderCreated); fsm.Start(); fsm.Fire(Events.Submitted); // Save this state to database 

你可以很容易地看到它的工作原理。

但我想在数据库中保存订单状态。 所以我将能够显示订单的状态。

我需要一个

 fsm.GetCurrentState() //show this state in the a table 

方法。 实际上有一种方法:我可以使用ExecuteOnEntry并在每个州的条目上更改本地值。 但是为每个州编写ExecuteOnEntry会很麻烦,因为我会重复自己!

必须有一种微妙的方式来做到这一点。

正如丹尼尔解释的那样,这是设计的。 让我解释一下原因:

状态机允许排队事件。 因此,向状态机询问其当前状态可能会产生误导。 它目前处于状态A,但已经有一个排队的事件将它转到状态B.

此外,我认为将状态机内部状态(您在状态机定义中使用的状态)直接与状态机外部状态(您希望在数据库中保留的状态)耦合在一起是不好的设计。 如果直接将这两者结合起来,就会失去在内部重构状态机而不影响外部(在您的情况下是数据库)的能力。 我经常遇到我必须将状态A拆分为A1和A2的情况,因为我必须对它们附加不同的操作,但是它们仍然表示对环境的相同状态。 因此,我强烈建议您将内部和外部状态分开,无论是使用OnEntryExecute()编写还是通过提供映射和使用扩展。 这是一个扩展,它将使您获得当前状态:

 public class CurrentStateExtension : ExtensionBase { public State CurrentState { get; private set; } public override void SwitchedState( IStateMachineInformation stateMachine, IState oldState, IState newState) { this.CurrentState = newState.Id; } } 

您可以通过以下方式将扩展添加到状态机:

 currentStateExtension = new CurrentStateExtension(); machine.AddExtension(currentStateExtension); 

当然,您也可以直接使用此扩展来访问当前状态。 为了使它更简单,让定义状态机的类实现扩展并将自身作为扩展传递。 让你摆脱额外的课程。

最后一点:当您在https://groups.google.com/forum/?fromgroups#!forum/appccelerate上的google群组中询问有关bbv.Common(或现在称为Appccelerate)的问题时,对我来说更容易找到问题并回答它;-)

这是设计的。 我们考虑查询状态机的状态作为设计气味。 但当然有例外情况。 您有以下两种选择:

  1. 使用ExecuteOnEntry方法保存订单的状态。 这反映了要走的路,因为你不要将状态机的状态泄漏到你的业​​务逻辑中。
  2. 编写自己的状态机装饰器,它使用内部StateMachine 。 这暴露了国家。

丹尼尔