11

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

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是只读属性,只能在实例化时设置。
  • myregistrationTrigger必须在状态机配置期间进行实例化,并且还必须可供调用类使用。

如何克服这些项目并封装状态机代码?

4

2 回答 2

7

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

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

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
    }
}
于 2017-03-26T07:44:14.653 回答
4

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

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

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);
    }
于 2017-03-28T04:30:32.333 回答