рдПрдХ рдмрд╛рд░, рд╣рдордиреЗ рдПрдХ рдмрдбрд╝реА рдХрдВрдкрдиреА рдореЗрдВ рд╡рд┐рднрд┐рдиреНрди рд╡рд░реНрдХрдлрд╝реНрд▓реЛрдЬрд╝ рдХреЛ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдХрд░рдиреЗ рдХреЗ рдХрд╛рд░реНрдп рдХрд╛ рд╕рд╛рдордирд╛ рдХрд┐рдпрд╛ред рд╣рдорд╛рд░реЗ рд▓рд┐рдП, рдЗрд╕рдХрд╛ рдорддрд▓рдм рд▓реЙрдиреНрдЪ рдХреЗ рд╕рдордп рд▓рдЧрднрдЧ 10 рд╕рд┐рд╕реНрдЯрдо рдХреЛ рдПрдХ рд╕рд╛рде рд░рдЦрдирд╛ рдерд╛ред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╕рдм рдХреБрдЫ рдЕрддреБрд▓реНрдпрдХрд╛рд▓рд┐рдХ рд░реВрдк рд╕реЗ, рд╕реНрдХреЗрд▓реЗрдмрд▓, рдордЬрд╝рдмреВрддреА рд╕реЗ рдЬреБрдбрд╝рд╛ рд╣реЛрдирд╛ рдерд╛ред
рд╕рд░рд▓реАрдХреГрдд рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рд╡рд┐рднрд┐рдиреНрди рдкреНрд░рдгрд╛рд▓рд┐рдпреЛрдВ рдореЗрдВ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рдЕрдиреБрдХреНрд░рдо рдХреЗ рд░реВрдк рдореЗрдВ рд╡рд░реНрдгрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдЬреЛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдирд╣реАрдВ рд╣реЛ рд╕рдХрддреЗ, рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕рдореЗрдВ рдорд╛рдирд╡ рдХреА рднрд╛рдЧреАрджрд╛рд░реА рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдХреБрдЫ рдХреНрд░рд┐рдпрд╛рдУрдВ рдпрд╛ рдкреНрд░рд╛рдердорд┐рдХ рд╕рдордиреНрд╡рдп рдХрд╛ рдЪрдпрди рдХрд░рдирд╛, рдЬреЛ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗ рдЕрдЧрд▓реЗ рдЪрд░рдг рдореЗрдВ рдЬрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реИред
рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдордиреЗ рдбреЗрдЯрд╛ рдмрд╕ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдореИрд╕реЗрдЬрд┐рдВрдЧ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛, рдФрд░ рд░реИрдмрд┐рдЯрдПрдордХреНрдпреВ рдХреЗ рд╕рд╛рде рдЕрдкрдиреА рдЧрд╛рдерд╛ рдХреЗ рд╕рд╛рде рдорд╛рд╕рдЯреНрд░рд╛рдВрд╕рд┐рдЯ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╣рдорд╛рд░реЗ рдЕрдиреБрдХреВрд▓ рд╣реИред

рдЬреИрд╕реЗ рд╕рд╛рдЧрд╛ рдХреНрдпрд╛ рд╣реИ?
рд╕рд╛рдЧрд╛ рдкреБрд╕реНрддрдХ
рдПрдВрдЯрд░рдкреНрд░рд╛рдЗрдЬрд╝ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЗрдВрдЯреАрдЧреНрд░реЗрд╢рди рдЯреЗрдореНрдкреНрд▓реЗрдЯ рд╕реЗ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдкреНрд░рдмрдВрдзрдХ рдЯреЗрдореНрдкрд▓реЗрдЯ рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИ, рдЬреЛ рдЖрдкрдХреЛ рдПрдХ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рд░рд╛рдЬреНрдп рдорд╢реАрди рдХреЗ рд░реВрдк рдореЗрдВ рд╡рд░реНрдгрд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рдкреНрд░рд╡реЗрд╢ рджреНрд╡рд╛рд░ рдкрд░ рдПрдХ рдШрдЯрдирд╛ рдЖрддреА рд╣реИ, рд╕рд╛рдЧрд╛ рдХреНрд░рд┐рдпрд╛рдУрдВ рдХрд╛ рдПрдХ рдХреНрд░рдо рдХрд░рддрд╛ рд╣реИред рдЙрд╕реА рд╕рдордп, рд╕рд╛рдЧрд╛ рдХреЗ рдХрд┐рд╕реА рднреА рдЪрд░рдг рдореЗрдВ, рдПрдХ рд╡реНрдпрдХреНрддрд┐ рдХреЗ рдирд┐рд░реНрдгрдп рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛ рд╕рдХрддреА рд╣реИред рдлрд┐рд░ рд╡рд╣ рдЯреНрд░реИрдХрд░ рдореЗрдВ рдПрдХ рдХрд╛рд░реНрдп рдмрдирд╛рддрд╛ рд╣реИ рдФрд░ рдЕрдирд┐рд╢реНрдЪрд┐рдд рд╕рдордп рдХреЗ рд▓рд┐рдП "рд╕реЛ рдЬрд╛рддрд╛ рд╣реИ", рдирдИ рдШрдЯрдирд╛рдУрдВ рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рддрд╛ рд╣реИред
рд╕рд╛рдЧрд╛ рдЖрдЯреЛрдореЗрдЯрд┐рдХ рдмреЗрдирд╛рдореА рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИред рдпрд╣ рдШреЛрд╖рд┐рдд рддреМрд░ рдкрд░ MassTransitStateMachine <> рд╕реЗ рд╡рд┐рд░рд╛рд╕рдд рдореЗрдВ рдорд┐рд▓реА рдПрдХ рдХрдХреНрд╖рд╛ рдореЗрдВ рд╡рд░реНрдгрд┐рдд рд╣реИред рд╕рд╛рдЧрд╛ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдХреБрдЫ рдШрдЯрдирд╛рдУрдВ рдХреЗ рд╣реЛрдиреЗ рдкрд░ рд╕рднреА рд░рд╛рдЬреНрдпреЛрдВ, рд▓реА рдЧрдИ рдШрдЯрдирд╛рдУрдВ рдФрд░ рдХрд┐рдП рдЧрдП рдХрд╛рд░реНрдпреЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рд╡рд░реНрддрдорд╛рди рд╕реНрдерд┐рддрд┐ рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рд╣реИред
рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдо рд╕рд╛рдЧрд╛ рдХреЗ рд╕рднреА рд░рд╛рдЬреНрдпреЛрдВ рдФрд░ рдШрдЯрдирд╛рдУрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЙрдиреНрд╣реЗрдВ рд╕рдордЭрдиреЗ рдпреЛрдЧреНрдп рдирд╛рдо рджреЗрддреЗ рд╣реИрдВред рдпрд╣ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:
public sealed partial class StateMachine { public State AwaitingTaskCreated { get; set; } public State AwaitingTaskTakedToWork { get; set; } public State AwaitingDecisionAboutTask { get; set; } public State Rejected { get; set; } public Event<IStartWorkflowCommand> StartWorkflowCommandReceived { get; set; } public Event<TaskCreatedNotification> TaskCreated { get; set; } public Event<TaskTakedToWorkNotification> TaskTakedToWork { get; set; } public Event<TaskDeclinedNotification> TaskDeclined { get; set; } public Event<TaskApprovedNotification> TaskApproved { get; set; } private void BuildStateMachine() { InstanceState(x => x.CurrentState); Event(() => StartWorkflowCommandReceived, x => x.CorrelateById(ctx => ctx.Message.CorrelationId) .SelectId(context => context.Message.CorrelationId)); Event(() => TaskCreated, x => x.CorrelateById(ctx => ctx.Message.CorrelationId)); Event(() => TaskTakedToWork, x => x.CorrelateById(ctx => ctx.Message.CorrelationId)); Event(() => TaskDeclined, x => x.CorrelateById(ctx => ctx.Message.CorrelationId)); Event(() => TaskApproved, x => x.CorrelateById(ctx => ctx.Message.CorrelationId)); } }
рд╣рдордиреЗ рдПрдХ рдЖрдВрд╢рд┐рдХ рд╡рд░реНрдЧ рд▓реЙрдиреНрдЪ рдХрд┐рдпрд╛ рд╣реИ, рдЬрд╣рд╛рдВ рд╣рдо рд╕рднреА рд░рд╛рдЬреНрдпреЛрдВ рдФрд░ рдШрдЯрдирд╛рдУрдВ рдХреА рдПрдХ рд╕реВрдЪреА рдФрд░ рдмрд┐рд▓реНрдбрд╕реНрдЯреИрдЯрдореИрдЪрд╛рдЗрди рд╡рд┐рдзрд┐ рдХреА рдШреЛрд╖рдгрд╛ рдХрд░рддреЗ рд╣реИрдВ, рдЬреЛ рд╕рд╛рдЧрд╛ рдХреЗ рд╕рд╛рде рдШрдЯрдирд╛рдУрдВ рдХреЗ рд╕рдВрдмрдВрдз рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдкреНрд░рддреНрдпреЗрдХ рдШрдЯрдирд╛ рдореЗрдВ рдПрдХ рд╡рд┐рд╢реЗрд╖ рдкреИрд░рд╛рдореАрдЯрд░ CorrelationId рдкрд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ - рдпрд╣ рдорд╛рд░реНрдЧрджрд░реНрд╢рд┐рдХрд╛ рд╣реИ, рдЬреЛ рд╕рднреА рдЬреБрдбрд╝реЗ рд╣реБрдП рд╕рд┐рд╕реНрдЯрдо рдФрд░ рдирд┐рдЧрд░рд╛рдиреА рдкреНрд░рдгрд╛рд▓рд┐рдпреЛрдВ рдХреЗ рдмреАрдЪ рдЪрд▓рддреА рд╣реИред
рдЗрд╕ рдкреНрд░рдХрд╛рд░, рдпрджрд┐ рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рдЙрддреНрдкрдиреНрди рд╣реЛрддреА рд╣реИ, рддреЛ рд╣рдо рд╕рднреА рдХрдиреЗрдХреНрдЯреЗрдб рд╕рд┐рд╕реНрдЯрдо рд╕реЗ рд▓реЙрдЧ рджреНрд╡рд╛рд░рд╛ рдХреНрдпрд╛ рд╣реЛ рд░рд╣рд╛ рд╣реИ рдХреА рдкреВрд░реА рддрд╕реНрд╡реАрд░ рдХреЛ рдкреБрдирд░реНрд╕реНрдерд╛рдкрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд╣рдо рд╕рд╛рдЧрд╛ рд╕реЗ рд╕рдВрджреЗрд╢реЛрдВ рдореЗрдВ CorrelationId рднреЗрдЬрддреЗ рд╣реИрдВ, рд╕рд┐рд╕реНрдЯрдо рдЗрд╕реЗ рд╕реВрдЪрдирд╛рдУрдВ рдореЗрдВ рд╡рд╛рдкрд╕ рднреЗрдЬрддреЗ рд╣реИрдВ рддрд╛рдХрд┐ рд╣рдо рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдЧрд╛рдерд╛ рдХреЗ рд╕рд╛рде рд╕рдВрджреЗрд╢ рдХреЛ рд╕рд╣рд╕рдВрдмрдВрдзрд┐рдд рдХрд░ рд╕рдХреЗрдВред
рдпрд╣рд╛рдБ рд╕реНрдЯреЗрдЯ рдорд╢реАрди рдХреНрд▓рд╛рд╕ рдХрд╛ рд╣реА рдПрдХ рдЙрджрд╛рд╣рд░рдг рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ:
public sealed partial class StateMachine : MassTransitStateMachine<WorkflowSaga> { public StateMachine() { BuildStateMachine(); Initially(WhenStartWorkflowCommandReceived()); During(AwaitingTaskCreatedInPlanner, WhenTaskCreated()); During(AwaitingTaskTakedToWork, WhenTaskTakedToWork()); During(AwaitingDecisionAboutTask, WhenTaskApproved(), WhenTaskDeclined()); } private EventActivityBinder<WorkflowSaga, IStartWorkflowCommand> WhenStartWorkflowCommandReceived() { return When(StartWorkflowCommandReceived) .Then(ctx => ctx.Instance.SaveConfigurationRequestInfo(ctx.Data)) .Send(TaskManagerQueue, ctx => new CreateTaskCommand(ctx.Instance)) .TransitionTo(AwaitingTaskCreated); } private EventActivityBinder<WorkflowSaga, TaskCreatedNotification> WhenTaskCreated() { return When(DPORPApproveTaskCreatedInPlanner) .Then(ctx => ctx.Instance.SaveCreatedTaskInfo(ctx.Data)) .Send(MailServiceQueue, ctx => new NotifyRequestAuthorThatWorkflowStarted(ctx.Instance)) .TransitionTo(AwaitingTaskTakedToWork); } private EventActivityBinder<WorkflowSaga, TaskTakedToWorkNotification> WhenTaskTakedToWork() { return When(TaskTakedToWork) .Then(ctx => ctx.Instance.MarkTaskAsTakedToWork(ctx.Data)) .TransitionTo(AwaitingDecisionAboutTask); } private EventActivityBinder<WorkflowSaga, TaskApprovedNotification> WhenTaskApproved() { return When(TaskApproved) .Then(ctx => ctx.Instance.MarkTaskAsApproved(ctx.Data)) .Finalize(); } private EventActivityBinder<WorkflowSaga, TaskDeclinedNotification> WhenTaskDeclined() { return When(TaskDeclined) .Then(ctx => ctx.Instance.MarkTaskAsDeclined(ctx.Data)) .TransitionTo(Rejected); } }
рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рд░рд╛рдЬреНрдпреЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИред рдкрдардиреАрдпрддрд╛ рдмрдирд╛рдП рд░рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рддреНрдпреЗрдХ рдШрдЯрдирд╛ рдХрд╛ рд░рд┐рд╕реЗрдкреНрд╢рди рдЕрд▓рдЧ рддрд░реАрдХреЗ рд╕реЗ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред рд╕рдВрджреЗрд╢реЛрдВ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХрд╛ рдкреВрд░рд╛ рддрд░реНрдХ рд╕реНрд╡рдпрдВ рд╕рдВрджреЗрд╢реЛрдВ рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЕрдиреНрдпрдерд╛, рд╕рд┐рд╕реНрдЯрдо рдХреА рдмрдврд╝рддреА рдЬрдЯрд┐рд▓рддрд╛ рдХреЗ рд╕рд╛рде, рд╕рд╛рдЧрд╛ рдХрд╛рдлреА рддреЗрдЬрд╝реА рд╕реЗ рд╕реВрдЬ рдЬрд╛рддрд╛ рд╣реИред
рд╕рдореНрдореЗрд▓рдиреЛрдВ рдХреЛ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдФрд░ рдкрдардиреАрдпрддрд╛ рдмрдирд╛рдП рд░рдЦрдиреЗ рдореЗрдВ рд╕рд╛рд╡рдзрд╛рдиреА рдмрд░рддрдиреА рдЪрд╛рд╣рд┐рдПред C # рдХреА рдЕрдкреВрд░реНрдгрддрд╛ рдХреЗ рдХрд╛рд░рдг, рдЗрд╕рдореЗрдВ рд░рд╛рдЬреНрдпреЛрдВ рдФрд░ рдХрд╛рд░реНрдпреЛрдВ рдХрд╛ рд╡рд┐рд╡рд░рдг рдШреЛрд╖рд┐рдд рдХрд░рдирд╛ рдмрд╣реБрдд рдореБрд╢реНрдХрд┐рд▓ рд╣реИред рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рд╕рд░рд▓ рд░рд╛рдЬреНрдп рдорд╢реАрдиреЛрдВ рдХреЗ рд▓рд┐рдП, рдЕрд╕рд▓реА рдирд░рдХ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИред
рдЕрдм рдХреБрдЫ рд╢рдмреНрдж SagaInstance рдХреЗ рдмрд╛рд░реЗ рдореЗрдВред SagaInstance рдПрдХ рд╡рд░реНрдЧ рд╣реИ рдЬрд┐рд╕реЗ SagaStateMachineInstance рд╕реЗ рд╡рд┐рд░рд╛рд╕рдд рдореЗрдВ рдорд┐рд▓рд╛ рд╣реИред рдЗрд╕рдореЗрдВ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдФрд░ рдлрд╝реАрд▓реНрдб рд╢рд╛рдорд┐рд▓ рд╣реЛрддреЗ рд╣реИрдВ рдЬреЛ рд░рд╛рдЬреНрдп рдорд╢реАрди рдХреА рд╡рд┐рд╢реЗрд╖рддрд╛ рд░рдЦрддреЗ рд╣реИрдВред рдореЛрдЯреЗ рддреМрд░ рдкрд░, рдпрд╣ рдЧрд╛рдерд╛ рдХреА рд╕реНрдореГрддрд┐ рд╣реИред рд╣рдо рд╕рднреА рд╕рд╛рдЧрд╛ рдбреЗрдЯрд╛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рддреЗ рд╣реИрдВ рдЬрд┐рд╕рдХреА рдЙрд╕реЗ рдЬреАрд╡рди рднрд░ рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреАред рд╕рд╛рде рд╣реА рдЗрд╕ рд╡рд░реНрдЧ рдореЗрдВ рдХрд╛рд░реНрдп рдХреЗ рджреМрд░рд╛рди рдЗрд╕ рдбреЗрдЯрд╛ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЗ рддрд░реНрдХ рдХрд╛ рд╡рд░реНрдгрди рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред
рдпрд╣рд╛рдБ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ:
public class WorkflowSaga : SagaStateMachineInstance , ISagaWithState , ICreatedOnOffset , IModifiedOnOffset , ICreatedBy<string> , IModifiedBy<string> { public Guid CorrelationId { get; set; } public string CurrentState { get; set; } public string InitialRequestViewUrl { get; set; } public string RequestNumber { get; set; } public string RequestAuthor { get; set; } public string RequestText { get; set; } public byte[] RowVersion { get; set; } public string CreatedBy { get; set; } public string ModifiedBy { get; set; } public DateTimeOffset CreatedOn { get; set; } public DateTimeOffset ModifiedOn { get; set; } public DateTimeOffset CompletedOn { get; set; } public virtual ICollection<RelatedTask> RelatedTasks { get; set; } public void SaveGabrielConfigurationRequestInfo( ICreateGabrielConfigurationRequestCommand command) { CorrelationId = command.CorrelationId; RequestNumber = command.RequestNumber; RequestAuthor = command.Author; RequestText = command.RequestText; InitialRequestViewUrl = command.InitialRequestViewUrl; CreatedOn = RuntimeContext.Current.DateTimeOffset.Now; } public void SaveCreatedTaskInfo(ITaskCreationInfo taskCreationInfo) { RelatedPlannerTasks.Add(new RelatedPlannerTask(taskCreationInfo)); } public void MarkTaskAsTakedToWork(ITaskUpdatedInfo taskInfo) { UpdateTaskInfo(taskInfo, TaskStatus.TakedToWork); } public void MarkTaskAsApproved(TaskApprovedNotification taskInfo) { UpdateTaskInfo(taskInfo, TaskStatus.Completed, taskInfo.Comment); CompletedOn = RuntimeContext.Current.DateTimeOffset.Now; } public void MarkTaskAsDeclined(TaskDeclinedNotification taskInfo) { UpdateTaskInfo(taskInfo, TaskStatus.Declined, taskInfo.Comment); CompletedOn = RuntimeContext.Current.DateTimeOffset.Now; } private void UpdateTaskInfo(ITaskUpdatedInfo taskInfo, TaskStatus taskStatus, string comment = null) { var task = RelatedTasks.Single(t => t.Number == taskInfo.Number); task.ModifiedBy = taskInfo.TaskModifiedBy; task.Comment = comment; task.Status = taskStatus; } }
рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╕реЗ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ SagaInstance CorrelationId рдореЗрдВ Saga рдФрд░ CurrentState рдХреЗ рд╕рд╛рде рдШрдЯрдирд╛рдУрдВ рдХреЗ рд╕рд╣рд╕рдВрдмрдВрдз рдХреЗ рд▓рд┐рдП рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ Saga рдХреА рд╡рд░реНрддрдорд╛рди рд╕реНрдерд┐рддрд┐ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣реИред
рд╣реИрдВрдбрд▓рд┐рдВрдЧ рдореЗрдВ рддреНрд░реБрдЯрд┐
рд╕рдВрджреЗрд╢ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рджреМрд░рд╛рди рддреНрд░реБрдЯрд┐ рд╣реЛрдиреЗ рдкрд░ рд╕рд╛рдЧрд╛ рдХрд╛ рдХреНрдпрд╛ рд╣реЛрддрд╛ рд╣реИ? рдпрд╣ рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдореБрджреНрджрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╣рд░ рдХреЛрдИ рдЪрд╛рд╣рддрд╛ рд╣реИ рдХрд┐ рд░рд╛рдЬреНрдп рдорд╢реАрди рд╣рдореЗрд╢рд╛ рд╕рдВрдЧрдд рд░рд╣реЗ, рднрд▓реЗ рд╣реА рдХреБрдЫ рдЧрд▓рдд рд╣реЛ рдЧрдпрд╛ рд╣реЛред рдФрд░ рдорд╛рд╕рдЯреНрд░рд╛рдВрд╕рд┐рдЯ рдХреЗ рд╕рд╛рде, рд╕рд╛рдЧрд╛ рдареАрдХ рдХрд░ рд░рд╣рд╛ рд╣реИред
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рдкрд╣рд▓реЗ рд╣реА рджреЗрдЦ рдЪреБрдХреЗ рд╣реИрдВ, рдКрдкрд░ рдХреЗ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдореЗрдВ рдЕрдкрд╡рд╛рджреЛрдВ рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рднреА рдХреЛрд╢рд┐рд╢ рдирд╣реАрдВ рдХреА рдЧрдИ рд╣реИред рдХрд╛рд░рдг рд╕рд░рд▓ рд╣реИ: рд╡реЗ рд╡рд╣рд╛рдБ рдХреА рдЬрд░реВрд░рдд рдирд╣реАрдВ рд╣реИред рдпрджрд┐ рд╕рдВрджреЗрд╢ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рджреМрд░рд╛рди рдХреЛрдИ рдЕрдкрд╡рд╛рдж рд╣реЛрддрд╛ рд╣реИ, рддреЛ рд╕рдВрджреЗрд╢ рдХрддрд╛рд░ рдореЗрдВ рд╡рд╛рдкрд╕ рдЖ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рд╕рднреА рдкрд░рд┐рд╡рд░реНрддрди рд╡рд╛рдкрд╕ рд▓реБрдврд╝рдХ рдЬрд╛рддреЗ рд╣реИрдВред рдЪреВрдВрдХрд┐ рд╣рдо рд╕рд╛рдЧрд╛ рдХреЗ рд╕рдорд╛рди рд▓реЗрдирджреЗрди рдореЗрдВ рд╕рднреА рдбреЗрдЯрд╛ рдЬреЛрдбрд╝рддреЛрдбрд╝ рдХрд░ рд░рд╣реЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд▓реЗрдирджреЗрди рдмрдВрдж рдирд╣реАрдВ рд╣реЛрдЧрд╛ред
рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, рд╕рд╛рдЧрд╛ рдореЗрдВ рд╕рд╛рдЧрд╛ рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдХрд┐рд╕реА рдЕрдиреНрдп рдЪреАрдЬрд╝ рдореЗрдВ рд╣реЗрд░рдлреЗрд░ рдХрд░рдирд╛ рдмреБрд░рд╛ рд╡реНрдпрд╡рд╣рд╛рд░ рд╣реИред "рдЗрдВрдЯрд┐рдЧреНрд░реЗрд╢рди рдЯреЗрдореНрдкреНрд▓реЗрдЯреНрд╕ рдлреЙрд░ рдПрдВрдЯрд░рдкреНрд░рд╛рдЗрдЬ рдПрдкреНрд▓рд┐рдХреЗрд╢рди" рдкреБрд╕реНрддрдХ рдХреЗ рдЕрдиреБрд╕рд╛рд░, рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдкреНрд░рдмрдВрдзрдХ рдЬрд┐рддрдирд╛ рд╕рдВрднрд╡ рд╣реЛ рдЙрддрдирд╛ рдкрддрд▓рд╛ рдФрд░ рдЧреВрдВрдЧрд╛ рд░рд╣рдирд╛ рдЪрд╛рд╣рд┐рдП: рдмрд╕ рд╕рд┐рд╕реНрдЯрдо рдХреЛ рдХрдорд╛рдВрдб рджреЗрдВ рдФрд░ рд╕реНрдерд┐рддрд┐ рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХрд░реЗрдВ, рд▓реЗрдХрд┐рди рдЙрд╕реЗ рдЦреБрдж рдХреБрдЫ рднреА рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред
рдмреЗрд╢рдХ, рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рдкрд░рд┐рджреГрд╢реНрдп рд╣реИрдВ рдЬрдм рдЖрдкрдХреЛ рдЕрдкрд╡рд╛рджреЛрдВ рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рдХреНрд╖рддрд┐рдкреВрд░реНрддрд┐ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдлрд┐рд░ "ред рдХреИрдЪ" рд╕реНрдЯреЗрдЯ рдорд╢реАрди рд╣реИрдВрдбрд▓рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рдкреНрд░рдХрд╛рд░ рдХреЗ рдЕрдкрд╡рд╛рдж рдХреЛ рдкрдХрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдлрд┐рд░ рдХреНрд╖рддрд┐рдкреВрд░реНрддрд┐ рддрд░реНрдХ рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рдФрд░ рдЕрдЧрд░ рдЖрдкрдХреЛ рдХреЗрд╡рд▓ рдПрдХ рдЕрдкрд╡рд╛рдж рдкреНрд░рддрд┐рдЬреНрдЮрд╛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рддреЛ рдПрдХ рдкрд░реНрдпрд╡реЗрдХреНрд╖рдХ (рдСрдмреНрдЬрд░реНрд╡рд░) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдмреЗрд╣рддрд░ рд╣реИред
рдЕрдм рдЙрд╕ рд╕реНрдерд┐рддрд┐ рдХреА рдХрд▓реНрдкрдирд╛ рдХрд░реЗрдВ, рдЬрд┐рд╕реЗ рд╣рдордиреЗ рд╕рдВрджреЗрд╢ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рджреМрд░рд╛рди рдкрд╣рд▓реЗ рд╣реА рднреЗрдЬреЗрдВ рдХрдорд╛рдВрдб рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░ рджрд┐рдпрд╛ рд╣реИ, рдЬрд┐рд╕рдХреЗ рдмрд╛рдж рдПрдХ рдЕрдкрд╡рд╛рдж рд╣реБрдЖ рд╣реИред рдЗрд╕ рдЪрд░рдг рдкрд░ рднреЗрдЬреЗ рдЧрдП рдЖрджреЗрд╢ рдХрд╛ рдХреНрдпрд╛ рд╣реЛрдЧрд╛? рд╕рдм рдХреЗ рдмрд╛рдж, рд╕рдм рдХреБрдЫ рдЬреЛ рдЙрдбрд╝ рдЧрдпрд╛ рд╣реИ рдЙрд╕реЗ рд╡рд╛рдкрд╕ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ? рд▓реЗрдХрд┐рди рдпрд╣рд╛рдВ рд╕рдм рдХреБрдЫ рд╕реЛрдЪ рд╕рдордЭ рдХрд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рдмрд╕ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рддреЗ рд╕рдордп, рдЖрдк UseInMemoryOutbox рд╡рд┐рдХрд▓реНрдк рдХреЛ рд╕рдХреНрд╖рдо рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдпрд╣ рд╡рд┐рдХрд▓реНрдк рдЖрдкрдХреЛ рд╡рд░реНрддрдорд╛рди рдЪрд░рдг рдкреВрд░рд╛ рд╣реЛрдиреЗ рддрдХ рд╕рдВрджреЗрд╢ рдирд╣реАрдВ рднреЗрдЬрдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рдпрджрд┐ рдЕрдкрд╡рд╛рдж рд╣реЛрддрд╛ рд╣реИ, рддреЛ рд╕рдВрджреЗрд╢ рдмрд┐рд▓реНрдХреБрд▓ рдирд╣реАрдВ рднреЗрдЬреЗ рдЬрд╛рдПрдВрдЧреЗред рдпрд╣рд╛рдБ рдкреНрд░рд▓реЗрдЦрди рд╕реЗ рдПрдХ рдЕрдВрд╢ рд╣реИ:
/// <summary> /// Includes an outbox in the consume filter path, which delays outgoing messages until the return path /// of the pipeline returns to the outbox filter. At this point, the message execution pipeline should be /// nearly complete with only the ack remaining. If an exception is thrown, the messages are not sent/published. /// </summary> /// <param name="configurator">The pipe configurator</param> public static void UseInMemoryOutbox(this IConsumePipeConfigurator configurator)
рдкрд░реАрдХреНрд╖рдг
рдкрд╣рд▓реА рдирдЬрд╝рд░ рдореЗрдВ, рдЕрддреБрд▓реНрдпрдХрд╛рд▓рд┐рдХ рд░рд╛рдЬреНрдп рдорд╢реАрди рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдЕрднреА рднреА рдПрдХ рдЦреБрд╢реА рд╣реИред рд▓реЗрдХрд┐рди рдпрд╣рд╛рдВ рд╕рдм рдХреБрдЫ рдареАрдХ рд╣реИред рдорд╛рд╕рдЯреНрд░рд╛рдВрд╕рд┐рдЯ рдПрдХ рдЕрдЪреНрдЫрд╛ рдкрд░реАрдХреНрд╖рдг рд▓реЗрдЦрди рдврд╛рдВрдЪрд╛ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ рдЬреЛ рд░рд╛рдЬреНрдп рдорд╢реАрди рдХреЗ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░реА рд╕рднреА рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдХреЛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдкреВрд░рд╛ рдХрд░рддрд╛ рд╣реИред
рдврд╛рдВрдЪрд╛ рдбреЗрдЯрд╛ рдмрд╕ (InMemoryTestHarness) рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд╕рд╛рде InMemory рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ, рдЬреЛ рдЖрдкрдХреЛ RabbitMQ рдпрд╛ рдЕрдиреНрдп рдХрддрд╛рд░ рдХреЛ рджрд░рдХрд┐рдирд╛рд░ рдХрд░рдХреЗ рд╕рдВрджреЗрд╢ рднреЗрдЬрдиреЗ рдФрд░ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред
рдЦреИрд░, рдПрдХ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд░реВрдк рдореЗрдВ:
[TestFixture] public class SagaTests : TestFixtureBase { protected const string HostName = "HostName"; protected InMemoryTestHarness Harness; protected StateMachine StateMachine; protected StateMachineSagaTestHarness<GabrielConfigurationRequestSaga, StateMachine> Saga; [SetUp] public void SetUp() { StateMachine = (StateMachine)Kernel. Get<MassTransitStateMachine<WorkflowSaga>>(); Harness = new InMemoryTestHarness(HostName); Saga = Harness .StateMachineSaga<WorkflowSaga, StateMachine>(StateMachine); } [TearDown] public async Task TearDown() { await Harness.Stop(); } protected async Task<WorkflowSaga> InitializeSaga() { await Harness.Start(); var command = new TestStartWorkflowCommand { CorrelationId = SagaId, Author = RequestAuthor, InitialRequestViewUrl = InitialRequestViewUrl, RequestText = RequestText, RequestNumber = RequestNumber, }; await Harness.InputQueueSendEndpoint .Send<IStartWorkflowCommand>(command); // , consume , // , Saga , Assert.IsTrue(Harness.Consumed .Select<IStartWorkflowCommand>().Any()); var currentSaga = Saga.Created.Contains(SagaId); currentSaga.RelatedPlannerTasks = new List<RelatedPlannerTask>(); return currentSaga; } [Test] public async Task CheckCurrntStateWhenStartWorkflowCommand() { var saga = await InitializeSaga(); Assert.IsNotNull(saga); Assert.AreEqual(StateMachine .AwaitingORDTApproveTaskCreatedInPlanner.Name, saga.CurrentState); } } public class WhenTaskCreated : SagaTestsBase { private async Task<WorkflowSaga> InitializeState() { var saga = await InitializeSaga(true); saga.CurrentState = StateMachine.AwaitingTaskCreated.Name; InitializeRelatedTask(saga); await SendTaskCreatedNotification(); Assert.IsTrue(Harness.Consumed .Select<TaskCreatedNotification>().Any()); return saga; } [Test] public async Task SaveWorkflowDataWhenTaskCreated() { var saga = await InitializeState(); var taskInfo = saga.RelatedPlannerTasks .First(task => task.PlannerTaskType == PlannerTaskType.DPORPApprove); Assert.AreEqual(TaskNumber, taskInfo.Number); Assert.AreEqual(TaskUrl, taskInfo.TaskUrl); Assert.AreEqual(SagaId, taskInfo.SagaCorrelationId); Assert.AreEqual(TaskStatus.Created, taskInfo.Status); Assert.AreEqual(User, taskInfo.ModifiedBy); Assert.AreEqual(saga.CurrentState, StateMachine.AwaitingTaskTakedToWork.Name); } [Test] public async Task SendsMailWhenTaskCreated() { var mailConsumer = Harness .Consumer<MockConsumer<ISendEmailMessageWithTemplateCommand>> (RabbitMqRouting.QueueNames .SendEmailsQueueName); await InitializeState(); Assert.IsTrue(mailConsumer.Consumed .Select<ISendEmailMessageWithTemplateCommand>().Any()); } private async Task SendTaskCreatedNotification() { await Harness.InputQueueSendEndpoint .Send(new TaskCreatedNotification { TaskUrl = TaskUrl, Number = TaskNumber, TaskModifiedBy = User, CorrelationId = SagaId }); } }
рдЯреЗрд╕реНрдЯ рдХрд╛рдлреА рддреЗрдЬ рджреМрдбрд╝рддреЗ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдПрдХ рдПрдХрд▓ рдбреЗрд╡рд▓рдкрд░ рдХреЗ рдХрдВрдкреНрдпреВрдЯрд░ рдкрд░, 850 рдкрд░реАрдХреНрд╖рдгреЛрдВ рдореЗрдВ рд▓рдЧрднрдЧ 21 рд╕реЗрдХрдВрдб рд▓рдЧрддреЗ рд╣реИрдВред
рдЙрдкрдпреЛрдЧреА рд╕реБрдЭрд╛рд╡
рдирд┐рд╖реНрдХрд░реНрд╖ рдореЗрдВ, рд╣рдо рдЕрдкрдиреЗ рдЕрдиреБрднрд╡ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЙрдкрдпреЛрдЧреА рд╕реБрдЭрд╛рд╡реЛрдВ рдХреА рдПрдХ рд╕реВрдЪреА рджреЗрддреЗ рд╣реИрдВред
рдХреЙрдиреНрдЯреНрд░реИрдХреНрдЯ рдФрд░ рдмрд╕ рд╕рдВрдЪрд╛рд░ рдпреЛрдЬрдирд╛рдПрдВ рдПрдХ рдирд┐рдЬреА рдирдЧреЗрдЯ рдореЗрдВ рд░рдЦреА рдЧрдИ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП рдЖрдкрдХреЛ рднреЗрдЬрдиреЗ рдФрд░ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдкрдХреНрд╖реЛрдВ рдХреЗ рдирд╛рдореЛрдВ рдореЗрдВ рдЕрдВрддрд░ рдирд╣реАрдВ рд╣реЛрдЧрд╛ред рдЖрдк рдирдЧреЗрдЯ рдореЗрдВ рдирд╛рдорд╛рдВрдХрд┐рдд рдХрддрд╛рд░реЛрдВ рдФрд░ рдореЗрдЬрдмрд╛рдиреЛрдВ рдХреЗ рд╕рд╛рде рд╕реНрдерд┐рд░рд╛рдВрдХ рднреА рд░рдЦ рд╕рдХрддреЗ рд╣реИрдВред рдирдЧреЗрдЯ рдкреНрд░рддрд┐ рджрд┐рди рдЕрдиреБрдХреВрд▓рди рдпреЛрдЧреНрдп рд╣реИред рдФрд░ рдХреБрдЫ рд╕реНрд░реЛрдд рдирд┐рдпрдВрддреНрд░рдг рдиреБрдЧреЗрдЯ рдХрд╛ рд╕рдорд░реНрдерди рднреА рдХрд░рддреЗ рд╣реИрдВ, рднреБрдЧрддрд╛рди рдХрд┐рдП рдЧрдП рдирд┐рдЬреА рдлрд╝реАрдб рд╣реИрдВред
рд╕реЗрдВрдб рдФрд░ рдкрдмреНрд▓рд┐рд╢ рдХреЗ рдмреАрдЪ рдХреЗ рдЕрдВрддрд░ рдХреЛ рд╕рдордЭреЗрдВред Send рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ рдпрджрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ рдПрдХ рдЧреНрд░рд╛рд╣рдХ рд╣реИ рдФрд░ рдЖрдк рдЙрд╕ рдХрддрд╛рд░ рдХрд╛ рдирд╛рдо рдЬрд╛рдирддреЗ рд╣реИрдВ рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рдЖрдк рдХрдорд╛рдВрдб рднреЗрдЬрддреЗ рд╣реИрдВред рдкреНрд░рдХрд╛рд╢рди рдкреНрд░рд╕рд╛рд░рдг рдЕрд▓рд░реНрдЯ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рд▓рд┐рдВрдХ рдкрд░ рд╡рд┐рд╡рд░рдгред
рдпрджрд┐ рдЖрдкрдХреЛ рдЕрдиреБрд░реЛрдз / рд░рд┐рд╕реНрдкрд╛рдВрд╕ рд╕рдВрджреЗрд╢ рдмрдирд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рддреЛ рдорд╛рд╕рдЯреНрд░рд╛рдВрд╕рд┐рдЯ рд╕реЗ рдЕрдиреБрд░реЛрдз / рд░рд┐рд╕реНрдкрд╛рдВрд╕ рдпреЛрдЬрдирд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рдЕрдиреБрдмрдВрдз рдХреА рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рдХрддрд╛рд░ рдирд╛рдо рдЬреЛрдбрд╝рдирд╛ рдмреЗрд╣рддрд░ рд╣реИ, рдЬрд┐рд╕реЗ рдорд╛рд╕рдЯреНрд░рд╛рдВрд╕рд┐рдЯ рдЦреБрдж рд╕реЗ рдмрдЪрдиреЗ рдХрд╛ рд╕реБрдЭрд╛рд╡ рджреЗрддрд╛ рд╣реИред рдЪреВрдВрдХрд┐ рдпрд╣ рд╡рд┐рд╢реНрд╡рд╕рдиреАрдпрддрд╛ рдХреЛ рдмрд╣реБрдд рдХрдо рдХрд░рддрд╛ рд╣реИред рдЖрдк рдЕрддреБрд▓реНрдпрдХрд╛рд▓рд┐рдХ рдХреЗ рд╕рднреА рд▓рд╛рднреЛрдВ рдХреЛ рдЦреЛ рд░рд╣реЗ рд╣реИрдВред рд▓реЗрдХрд┐рди рдЕрдЧрд░ рдЖрдкрдХреЛ рдЕрднреА рднреА рд╕реАрдорд┐рдд рд╕рдордп рдореЗрдВ рдЬрд╡рд╛рдм рдкрд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рддреЛ рд╕реАрдзреЗ рдХреЙрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдмреЗрд╣рддрд░ рд╣реИред рдпрд╣ рдПрдХ рд╣реА рдкреБрд╕реНрддрдХ рдореЗрдВ рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рд╣реИ, "рдПрдВрдЯрд░рдкреНрд░рд╛рдЗрдЬ рдПрдкреНрд▓реАрдХреЗрд╢рди рдЗрдВрдЯреАрдЧреНрд░реЗрд╢рди рдЯреЗрдореНрдкреНрд▓реЗрдЯред"
рдЧрд╛рдерд╛ рдкрддрд▓реА рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдПред рдЕрдиреНрдп рдкреНрд░рдгрд╛рд▓рд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рд╕рднреА рднрд╛рд░реА рддрд░реНрдХ рд▓реЗ рдЬрд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░реЗрдВред рдФрд░ рд╕рд╛рдЧрд╛ рдХреЛ рд░рд╛рдЬреНрдпреЛрдВ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХреВрджрдирд╛ рдЪрд╛рд╣рд┐рдП рдФрд░ рдмрд╛рдПрдВ рдФрд░ рджрд╛рдПрдВ рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рддрд┐рддрд░ рдмрд┐рддрд░ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред
рд╕рднреА рд╕рдВрджреЗрд╢ CorrelationId рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ, рдЬреЛ рд╕рд┐рд╕реНрдЯрдо рдХреЗ рдмреАрдЪ рдЪрд▓реЗрдЧрд╛ред рддреЛ рдлрд┐рд░ рд▓реЙрдЧ рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░рдирд╛ рдФрд░ рд╕рднреА рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рдПрдХ рд╣реА рддрд╕реНрд╡реАрд░ рдореЗрдВ рд▓рд┐рдВрдХ рдХрд░рдирд╛ рдмрд╣реБрдд рдЖрд╕рд╛рди рд╣реИред рдорд╕реНрд╕реНрдЯреНрд░рд╛рдВрд╕рд┐рдЯ рднреА рдпрд╣реА рдХрд░рддрд╛ рд╣реИред CorrelationBy рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рд╕реЗ рд╡рд┐рд░рд╛рд╕рдд рдореЗрдВ рдорд┐рд▓рдиреЗ рдкрд░ рд╕рдВрджреЗрд╢реЛрдВ рдореЗрдВ CorrelationId рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИред
рдЕрдкрдиреЗ рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рд▓реЙрдЧ рдФрд░ рдореЙрдирд┐рдЯрд░ рд╕реЗрдЯ рдЕрдк рдХрд░реЗрдВ, рдпрд╣ рдХрднреА рднреА рдЪреЛрдЯ рдирд╣реАрдВ рдкрд╣реБрдВрдЪрд╛рдПрдЧрд╛ред рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рд╣рдорд╛рд░рд╛ рдЕрдиреБрднрд╡ред