10

在使用c#无状态库时,人们如何构造代码?无状态状态机库 - 适当的结构方式?

https://github.com/nblumhardt/stateless

我如何特别感兴趣这种关系与注入的依赖关系,以及责任的正确的方法和正确的层次感。

我现在的结构涉及以下内容:

public class AccountWf 
{ 
    private readonly AspNetUser aspNetUser; 

    private enum State { Unverified, VerificationRequestSent, Verfied, Registered } 
    private enum Trigger { VerificationRequest, VerificationComplete, RegistrationComplete } 

    private readonly StateMachine<State, Trigger> machine; 

    public AccountWf(AspNetUser aspNetUser, AccountWfService userAccountWfService) 
    { 
     this.aspNetUser = aspNetUser; 

     if (aspNetUser.WorkflowState == null) 
     { 
      aspNetUser.WorkflowState = State.Unverified.ToString(); 
     } 

     machine = new StateMachine<State, Trigger>(
     () => (State)Enum.Parse(typeof(State), aspNetUser.WorkflowState), 
     s => aspNetUser.WorkflowState = s.ToString() 
     ); 

     machine.Configure(State.Unverified) 
     .Permit(Trigger.VerificationRequest, State.VerificationRequestSent); 

     machine.Configure(State.VerificationRequestSent) 
     .OnEntry(() => userAccountWfService.SendVerificationRequest(aspNetUser)) 
     .PermitReentry(Trigger.VerificationRequest) 
     .Permit(Trigger.VerificationComplete, State.Verfied); 

     machine.Configure(State.Verfied) 
     .Permit(Trigger.RegistrationComplete, State.Registered); 

    } 

    public void VerificationRequest() 
    { 
     machine.Fire(Trigger.VerificationRequest); 
    } 

    public void VerificationComplete() 
    { 
     machine.Fire(Trigger.VerificationComplete); 
    } 

    public void RegistrationComplete() 
    { 
     machine.Fire(Trigger.RegistrationComplete); 
    } 

} 

我们应该实现所有进程OnEntry钩内(调用服务),或实施在外面的过程之后的状态转换已经证实,它是允许发生?如果是的话,我很好奇如何做交易管理。

我想我以后是从那些已经实现了使用无状态的东西,以及如何处理代码结构的人的一些最好的指导。

+0

看着这一点,我倾向于使用注入到域服务的工厂来构造工作流对象,并且这可以传递工作流对象所需的服务。 – dandcg 2014-10-09 15:44:39

+0

仍在寻找关于使用状态机的最佳方法的一些指导。假设我需要在发送电子邮件服务时调用一个方法,该方法在Web请求的整个生命周期中都存在。这个呼叫应该在OnEntry中还是在公共方法中进行。如果它在OnEntry中如果在转换过程中遇到问题会发生什么?有些人已经使用无状态实现代码,并且他们已经放置了实际代码的一些指导将不胜感激。 – dandcg 2014-10-12 15:55:24

回答

11

解决结构本身一对夫妇的言论之前:

  • OnEntry操作仅执行如果触发已成功发射。

  • 在当前状态下不允许触发的触发器将抛出一个InvalidOperationException。如果您不期待发生异常(我发现记录未处理的触发器是找到逻辑中的缺陷的好方法),请考虑覆盖OnUnhandledTrigger

我的经验为OnEntry/OnExit结构的规则是,任何创造和逻辑将被放置OnEntry和任何所需的清理做OnExit

所以在你的情况下,考虑到你正在使用注入的依赖关系(并假设你没有取得那些所有权,即别人会管理他们的生命周期),你可以放置所有的逻辑OnEntry

考虑到这一点,你的状态机目前的结构方式非常好。最后一个注意事项,请记住,在同一个线程内触发触发器,它将推进状态机和执行状态机逻辑,并可能导致堆栈溢出异常(有关如何解决自动超前问题,请参阅here)。

+0

嗨Omni,谢谢你。如果在OnEntry实现期间发生错误,那么会发生什么? - 状态是否会改变?你也一般会用工厂来创建工作流实例吗?这将处理新的wf实例的起始状态并传递实现所需的依赖关系? – dandcg 2014-10-15 10:12:28

+0

Hi @dandcg。状态转换发生在'OnEntry'处理之前,因此在抛出异常时,状态已经改变。然后您必须决定在哪里处理异常。在'OnEntry'或者'machine.Fire(...)'内部转换成抛出异常的状态。 使用工厂创建'AccountWf'我没有太多说的。如果取决于参数,工厂将很有用,您有不同类型的机器/配置。 – Omni 2014-10-15 10:33:06

+0

工厂的原因是我不想注入工作流实例本身?但我想这是好的。你是怎么玩的? – dandcg 2014-10-15 10:36:27