2016-10-05 30 views
1

我有一個控制器傳奇,曾經有一個步驟在一個事務中啓動一個包含3個動作的進程。我現在正在將這個子過程重構爲一個單獨的傳奇。這樣做的結果將是原始傳奇將會啓動新的「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(); 
    } 
} 

我看到的唯一的溶劑是改變subsaaga生成一個單獨的correlationId以及保持原始號。例如:

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,當SubSaga完成時通知MainSaga。與此問題是,另一個消費者也正在發送MySubProcessCommand而不等待完成的事件/答覆。

回答

3

最好的做法是在sub-saga中使用ReplyToOriginator()來回傳給主要的傳奇。這種方法在佐賀基礎類上暴露出來。

有兩種方法可以解決主傳奇和不同發起者啓動子傳奇的問題。

  1. 使用兩個不同的命令。

讓兩個不同的命令啓動子的傳奇,像 MySubProcessFromMainSagaCommandMySubProcessFromSomewhereElseCommand。佐賀有多個IAmStartedByMessages<>是很好的。

  • 擴展MySubProcessCommand
  • 包括在MySubProcessCommand一些數據,以指示它是否從主佐賀或其它引發劑來了。

    無論採用哪種方式,都會爲您提供足夠的信息來存儲子傳奇的啓動方式,例如Data.WasInitatedByMainSaga。在子傳奇完成邏輯中檢查它。如果確實如此,請ReplyToOriginator()與原始主要傳奇交流。如果沒有,請跳過回覆。

    +0

    感謝您的回答。然後,這個sub-saga將不得不創建它自己的相關ID,我猜是okei。通常情況下,我的傳奇使用startsBy消息中的相關ID,現在看來這是錯誤的,因爲這種情況存在。 – sp1nakr

    +0

    在我們的案例中,回覆將不起作用,因爲傳奇的最後一步是處理來自其他服務的消息的消息處理程序。回覆消息將因此被指向該端點,而不是主要傳奇的端點。建議使用Bus.SendLocal()方法嗎?否則,除了發佈「流程完成事件」或其他類似的問題,我不會看到另一種解決方案。 – sp1nakr

    +0

    你是對的@ sp1nakr。 :)調用的正確方法是'ReplyToOriginator()',而不是'Bus.Reply()'。我已經更新了我的答案。您描述的情況與https://docs.particular.net/nservicebus/sagas/reply-replaytooriginator-differences和https://docs.particular.net/nservicebus/sagas/#notifying-callers-of-status相同。 – janovesk

    相關問題