如何正确关联启动另一个控制器传奇的多个实例的控制器传奇?

时间:2016-10-05 09:14:06

标签: c# nservicebus correlation nservicebus-sagas

我有一个控制器传奇,过去常常在一个事务中启动一个包含3个动作的进程。我现在正在将这个子流程重构为一个单独的传奇。这样做的结果是原始的saga将启动新的“sub-saga”的多个实例(这个sub-saga也将由其他非传奇进程启动,通过相同的命令)。我的问题是如何以最好的方式关联这种传奇的层次结构?

在下面的示例中,主要传奇将尝试使用相同的correlationId启动子传奇的三个实例。即使这样做,3个实例也会通过处理源自所有实例的“已完成事件”而相互干扰。

public class MyMainSaga : Saga<MyMainSagaData>, 
    IAmStartedByMessages<MyMainCommand>,
    IHandleMessage<MySubProcessCommandCompletedEvent>
{
    protected override void ConfigureHowToFindSaga(SagaPropertyMapper<MyMainSagaData> mapper)
    {
        mapper.ConfigureMapping<MyMainCommandCompletedEvent>(message => message.CorrelationId).ToSaga(data => data.CorrelationId);
    }

    public void Handle(MyMainCommand message)
    {
        Data.CorrelationId = message.CorrelationId;
        foreach (var item in message.ListOfObjectsToProcess)
        {
            Bus.Send(new MySubProcessCommand{
                CorrelationId = Data.CorrelationId,
                ObjectId = item.Id
            });
        }
    }

    public void Handle(MySubProcessCommandCompletedEvent message)
    {
        SetHandledStatus(message.ObjectId);
        if(AllObjectsWhereProcessed())
            MarkAsComplete();     
    }       
}


public class MySubSaga : Saga<MySubSagaData>, 
    IAmStartedByMessages<MySubProcessCommand>,
    IHandleMessage<Step1CommandCompletedEvent>,
    IHandleMessage<Step2CommandCompletedEvent>,
    IHandleMessage<Step3CommandCompletedEvent>
{
    protected override voidConfigureHowToFindSaga(SagaPropertyMapper<MySubSagaData> mapper)
    {
        mapper.ConfigureMapping<Step1CommandCompletedEvent>(message => message.CorrelationId).ToSaga(data => data.CorrelationId);
        mapper.ConfigureMapping<Step2CommandCompletedEvent>(message => message.CorrelationId).ToSaga(data => data.CorrelationId);
        mapper.ConfigureMapping<Step3CommandCompletedEvent>(message => message.CorrelationId).ToSaga(data => data.CorrelationId);
    }

    public void Handle(MySubProcessCommand message)
    {
        Data.CorrelationId = message.CorrelationId;
        Data.ObjectId = message.ObjectId;
        Bus.Send(new Step1Command{
            CorrelationId = Data.CorrelationId;  
        });
    }

    public void Handle(Step1CommandCompletedEvent message)
    {
        Bus.Send(new Step2Command{
            CorrelationId = Data.CorrelationId;  
        });
    }

    public void Handle(Step2CommandCompletedEvent message)
    {
        Bus.Send(new Step3Command{
            CorrelationId = Data.CorrelationId;  
        });
    }

    public void Handle(Step3CommandCompletedEvent message)
    {
        Bus.Publish<MySubProcessCommandCompletedEvent>(e => {
            e.CorrelationId = Data.CorrelationId;
            e.ObjectId = Data.ObjectId;
        });
        MarkAsComplete();
    }   
}

我看到的唯一解决方法是更改​​子传奇以生成单独的correlationId以及保持创建者ID。 E.g:

    public void Handle(MySubProcessCommand message)
    {
        Data.CorrelationId = Guid.NewGuid();
        Data.OriginatorCorrelationId = message.CorrelationId;
        Data.ObjectId = message.ObjectId;
        Bus.Send(new Step1Command{
            CorrelationId = Data.CorrelationId;  
        });
    }
    public void Handle(Step1CommandCompletedEvent message)
    {
        Bus.Send(new Step2Command{
            CorrelationId = Data.CorrelationId;  
        });
    }

    public void Handle(Step2CommandCompletedEvent message)
    {
        Bus.Send(new Step3Command{
            CorrelationId = Data.CorrelationId;  
        });
    }

    public void Handle(Step3CommandCompletedEvent message)
    {
        Bus.Publish<MySubProcessCommandCompletedEvent>(e => {
            e.CorrelationId = Data.OriginatorCorrelationId;
            e.ObjectId = Data.ObjectId;
        });
        MarkAsComplete();
    } 

这个问题是否有“最佳实践”解决方案?我一直在考虑使用Bus.Reply,当子传奇完成时通知MainSaga。问题是另一个消费者也在不等待已完成的事件/回复的情况下发送MySubProcessCommand。

1 个答案:

答案 0 :(得分:3)

最佳做法是在子传奇中使用ReplyToOriginator()与主要传奇进行沟通。这种方法暴露在Saga基类上。

有两种方法可以解决主要传奇和不同发起者启动子传奇的问题。

  1. 使用两个不同的命令。
  2. 让两个不同的命令启动子传奇,比如 MySubProcessFromMainSagaCommandMySubProcessFromSomewhereElseCommand。对于Saga来说,可以有多个IAmStartedByMessages<>

    1. 延长MySubProcessCommand
    2. MySubProcessCommand中包含一些数据,以表明它是来自主要传奇还是其他发起人。

      无论哪种方式都会为您提供足够的信息来存储子传奇的启动方式,例如Data.WasInitatedByMainSaga。在子传奇完成逻辑中检查这一点。如果是,请执行ReplyToOriginator()与原始主传奇进行通信。如果没有,请跳过回复。