【问题标题】:How to encapsulate .NET Stateless state machine如何封装 .NET 无状态状态机
【发布时间】:2017-03-28 12:30:32
【问题描述】:

我有一个项目,其中大部分是线性工作流程。我正在尝试使用 .NET Stateless library 作为工作流引擎/状态机。示例数量有限,但我整理了以下代码:

private StateMachine<WorkflowStateType, WorkflowStateTrigger> stateMachine;
private StateMachine<WorkflowStateType, WorkflowStateTrigger>.TriggerWithParameters<Guid, DateTime> registrationTrigger;
private Patient patient;

public Patient RegisterPatient(DateTime dateOfBirth)
{
    configureStateMachine(WorkflowState.Unregistered);
    stateMachine.Fire<DateTime>(registrationTrigger, dateOfBirth);
    logger.Info("State changed to: " + stateMachine.State);
    return patient;
}

private void configureStateMachine(WorkflowState state)
{
    stateMachine = new StateMachine<WorkflowState, WorkflowTrigger>(state);

    registrationTrigger = stateMachine.SetTriggerParameters<DateTime>(WorkflowTrigger.Register);

    stateMachine.Configure(WorkflowState.Unregistered)
        .Permit(WorkflowTrigger.Register, WorkflowStateType.Registered);

    stateMachine.Configure(WorkflowState.Registered)
        .Permit(WorkflowTrigger.ScheduleSampling, WorkflowState.SamplingScheduled)
        .OnEntryFrom(registrationTrigger, (dateOfBirth) => registerPatient(dateOfBirth));
}

private void registerPatient(DateTime dateOfBirth)
{
    //Registration code
}

如您所见,我正在使用 Stateless Fire() 重载,它允许我传入触发器。这样我就可以让状态机处理业务逻辑,在这种情况下,是注册新患者的代码。

这一切都有效,但现在我想将所有状态机代码移动到另一个类中以封装它,但我在执行此操作时遇到了麻烦。我在此过程中遇到的挑战是:

  • 实例化StateMachine 对象需要您指定状态,而State 是只读属性,只能在实例化时设置。
  • 我的registrationTrigger 必须在状态机配置期间进行实例化,并且还必须可供调用类使用。

如何克服这些问题并封装状态机代码?

【问题讨论】:

    标签: c# stateless-state-machine


    【解决方案1】:

    Scott Hanselman 有一个article,其中包含一个示例和对图书馆的介绍。他们的 GitHub 上也有一些可用的示例,包括 Scott 的封装状态机的文章中提到的 Bug implementation example

    以下是如何从行为中提取状态的示例:

    public class PatientRegistrationState
    {
        private StateMachine<WorkflowState, WorkflowTrigger> stateMachine;
        private StateMachine<WorkflowState, WorkflowStateTrigger>.TriggerWithParameters<DateTime> registrationTrigger;
    
        public PatientRegistrationState(State initialState = default(State)) {
            stateMachine = new StateMachine<WorkflowState, WorkflowTrigger>(initialState);
    
            stateMachine.Configure(WorkflowState.Unregistered)
                .Permit(WorkflowTrigger.Register, WorkflowStateType.Registered);
    
            stateMachine.Configure(WorkflowState.Registered)
                .Permit(WorkflowTrigger.ScheduleSampling, WorkflowState.SamplingScheduled)
                .OnEntryFrom(registrationTrigger, (date) => OnPatientRegistered(date));
        }
    
        public WorkflowState State => stateMachine.State;
        public Action<DateTime> OnPatientRegistered {get; set;} = (date) => { };
    
        // For state changes that do not require parameters.
        public void ChangeTo(WorkflowTrigger trigger)
        {
            stateMachine.Fire<DateTime>(trigger);
        }
    
        // For state changes that require parameters.
        public void ChangeToRegistered(DateTime dateOfBirth)
        {
            stateMachine.Fire<DateTime>(registrationTrigger, dateOfBirth);        
        }
    
        // Change to other states that require parameters...
    }
    
    public class PatientRegistration
    {
        private PatientRegistrationState registrationState;
        private Patient patient;
    
        public PatientRegistration()
        {
            registrationState = PatientRegistrationState(WorkflowState.Unregistered)
            {
                OnPatientRegistered = RegisterPatient;
            }
        }
    
        public Patient RegisterPatient(DateTime dateOfBirth)
        {
            registrationState.ChangeToRegistered(dateOfBirth);
            logger.Info("State changed to: " + registrationState.State);
            return patient;
        }
    
        private void RegisterPatient(DateTime dateOfBirth)
        {
            // Registration code
        }
    }
    

    【讨论】:

    • Hanselman 的文章是我最初实现的基础,我在 github 上看到了这个示例。两者都涉及与消费者在同一类中的状态机代码。
    • @im1dermike 我想我理解你想要达到的目标。请查看我对答案所做的修改。
    • 现在正在尝试实施。我认为您在此行中输入:public WorkflowState State { get; } =&gt; stateMachine.State;
    • 感谢您的回答。我能够在我的项目中实现这一点并且它有效。最终,我认为与所获得的相比,使用 Stateless 的开销太大了。
    • @im1dermike 您能否解释一下或更好地添加有关您如何解决问题的答案?我现在的情况和你一样
    【解决方案2】:

    这就是我在项目中实现它的方式。

    分离的工作流逻辑来分离类。我有几个基于请求对象中存在的标志之一的工作流;以下是工作流类之一:

    public class NationalWorkflow : BaseWorkflow
    {
        public NationalWorkflow(SwiftRequest request) : this(request, Objects.RBDb)
        { }
    
        public NationalWorkflow(SwiftRequest request, RBDbContext dbContext)
        {
            this.request = request;
            this.dbContext = dbContext;
            this.ConfigureWorkflow();
        }
    
        protected override void ConfigureWorkflow()
        {
            workflow = new StateMachine<SwiftRequestStatus, SwiftRequestTriggers>(
               () => request.SwiftRequestStatus, state => request.SwiftRequestStatus = state);
    
            workflow.OnTransitioned(Transitioned);
    
            workflow.Configure(SwiftRequestStatus.New)
                .OnEntry(NotifyRequestCreation)
                .Permit(SwiftRequestTriggers.ProcessRequest, SwiftRequestStatus.InProgress);
    
            workflow.Configure(SwiftRequestStatus.InProgress)
                .OnEntry(ValidateRequestEligibility)
                .Permit(SwiftRequestTriggers.AutoApprove, SwiftRequestStatus.Approved)
                .Permit(SwiftRequestTriggers.AdvancedServicesReview, SwiftRequestStatus.PendingAdvancedServices);
    
    .....................
    }
    

    从控制器/任何其他层触发:

    private static void UpdateRequest(SwiftRequestDTO dtoRequest)
        {
                var workflow = WorkflowFactory.Get(request);
                workflow.UpdateRequest();
        }
    

    如上所述,我根据请求对象中的条件有不同的工作流规则,因此使用了工厂模式WorkflowFactory.Get(request);您可以根据需要创建工作流实例/注入它

    在工作流类(在我的例子中是 BaseWorkflow 类)中,我公开了这些操作:

        public void UpdateRequest()
        {
            using (var trans = this.dbContext.Database.BeginTransaction())
            {
                this.actionComments = "Updating the request";
                this.TryFire(SwiftRequestTriggers.Update);
    
                SaveChanges();
                trans.Commit();
            }
        }
    
      protected void TryFire(SwiftRequestTriggers trigger)
        {
            if (!workflow.CanFire(trigger))
            {
                throw new Exception("Cannot fire " + trigger.ToString() + " from state- " + workflow.State);
            }
            workflow.Fire(trigger);
        }
    

    【讨论】:

      猜你喜欢
      • 2013-08-27
      • 2018-12-19
      • 2013-11-10
      • 2011-04-18
      • 1970-01-01
      • 1970-01-01
      • 2010-10-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多