Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested Workflows #60

Closed
rsnj opened this issue Oct 3, 2017 · 17 comments
Closed

Nested Workflows #60

rsnj opened this issue Oct 3, 2017 · 17 comments
Assignees

Comments

@rsnj
Copy link

rsnj commented Oct 3, 2017

I just came across your library and it looks great! I am doing some research to see if it will fit my requirements.

This is a two part question:

  1. Is it possible to nest workflows, so that one workflow starts another workflow and waits for it to finish with a result of some kind?

  2. Is it possible to correlate a given workflow so that two workflows with a given correlation id cannot be started concurrently?

My use case is essentially a parent / child relationship with some long running processes.
I want to create a RegisterChildWorkflow and from within that workflow I want to kickoff the RegisterParentWorkflow, but there should only ever be one RegisterParentWorkflow running at a time. So that if I register two children they would both subscribe to the same RegisterParentWorkflow result.

Thanks!

@danielgerlag
Copy link
Owner

  1. Currently, there is no built in functionality to explicitly nest workflows, but you could compose the aggregate the fluent call chains into variables that you could reuse as sort of "sub-workflows" - However, this is definitely on my radar as a first class feature.

  2. You could write a custom locking step to achieve this but there is no built in feature for it.

@ewyuen
Copy link

ewyuen commented Nov 30, 2017

You mention you can compose the fluent call chains into variables and reuse it. Can you provide an example how to do it?

@danielgerlag
Copy link
Owner

Something like this

public void Build(IWorkflowBuilder<MyData> builder)
{
    var branch1 = new Action<IWorkflowBuilder<MyData>>(branch => 
        branch.StartWith<PrintMessage>()
            .Input(step => step.Message, data => "Value is less than 3"));

    var branch2 = new Action<IWorkflowBuilder<MyData>>(branch =>
        branch.StartWith<PrintMessage>()
            .Input(step => step.Message, data => "Value is less than 5"));

    builder
        .StartWith<SayHello>()
        .If(data => data.Counter < 3).Do(branch1)
        .If(data => data.Counter < 5).Do(branch2)
        .Then<SayGoodbye>();
}        

@ewyuen
Copy link

ewyuen commented Dec 1, 2017

Thank you! It works.

@ScarletyCapone
Copy link

Is it possible for these branches to utilize different context classes other than "MyData". EX the main workflow use a "MasterWorkflowDataContext" and the branches have "Branch1Context", "Branch2Context", etc. allowing us to pass some data back and forth between the master and branch data context classes? I'd like not to bloat "MyData" with too many properties that only pertain to a branch.

@danielgerlag
Copy link
Owner

That's a good suggestion... I will investigate what is involved to implement something like this

@rose-pace
Copy link

I have a similar issue but mine relates to working within a loop. Essentially I am pulling a batch of data and looping through it and working on individual messages. Based on message type my logic branches to perform different tasks. In the end I create a new entity that gets saved.

It is cumbersome passing data around via context.Item inside the loop and it would be nice if you could set up a different data class within the loop so I could pass more data between steps than just context.Item.

@ScarletyCapone
Copy link

ScarletyCapone commented Nov 28, 2018

@rosspace Within your loop you still have access to the context class used for the entire workflow, you're not limited to just the item within a collection. So if your context class contains a class to persist data between the steps and a collection of classes for your messages to process, you can still get to everything.

@rose-pace
Copy link

rose-pace commented Nov 28, 2018

@ScarletyCapone but if I'm in a parallel ForEach I could set fields on the context class but wouldn't that not be thread safe? Since other iterations could be running on separate threads couldn't anything I assign be overwritten by another thread between steps? Or are you saying I should be using a While loop and just increment an index of the item I am working on that way I can set whatever I want on the main class?

The while loop approach would work although it would become a synchronous process instead of parallel but that is probably not a problem in my case.

@ScarletyCapone
Copy link

@rosspace If I understood right these are not parallel in the essence that they run on different threads; I had the same confusions over in issue 189 that I opened. I also learned this ForEach implementation doesn't iterate as one would think, it will do step 1 for all items in your collection before attempting step 2 for any of them, which is why I moved to a while loop for my solution. I needed to logically process all tasks for item 1 before attempting any for the next.

@rose-pace
Copy link

@ScarletyCapone that is good to know. I would not have expected it to work that way. I don't think that would give me an issue but I think I will want to move to the while loop anyways because of the complexity of my workflow.

Thanks!

@danielgerlag danielgerlag self-assigned this Nov 29, 2018
@ScarletyCapone
Copy link

@danielgerlag Circling back to this finally, did you have any additional thoughts on branches that can utilize different data contexts? Trying to build some reusable branches for common sets of tasks that may be shared among various workflows. The workflows all use different data types, but they all inherit from base classes / interfaces that would be utilized for the branches.

@danielgerlag
Copy link
Owner

@ScarletyCapone I have not spent much time on this yet but I think it is worthwhile

@gonengf
Copy link

gonengf commented May 31, 2019

I also think that would be great to have branches to utilize different context classes

There is no way at this point to pass a specific object in a ForEach loop to a re-usable sub-workflow apart from using one in the main data context class.

CORRECTION:

I could access the looped object under the sub-workflow using:

  Action<IWorkflowBuilder<ModuleCompareWorkflowData>> installWorkflow = new Action<IWorkflowBuilder<ModuleCompareWorkflowData>>(workflow =>
                workflow.StartWith<LogMessageStep>()
                    .Input(step => step.Message, (data, context) => ((ModuleToInstall)context.Item).Name));

@alex-vorobyev
Copy link

alex-vorobyev commented Nov 21, 2019

  1. Currently, there is no built in functionality to explicitly nest workflows, but you could compose the aggregate the fluent call chains into variables that you could reuse as sort of "sub-workflows" - However, this is definitely on my radar as a first class feature.

@danielgerlag so, is native support for nested workflows planned?

@danielgerlag
Copy link
Owner

#486

@mitbilal
Copy link

        builder
            .StartWith<WorkFlowStarted>()
            .WaitFor("MyEvent1", (data, context) => "Key", data => DateTime.Now)
                .Output(data => data.Value1, step => step.EventData)
            .Then((context) =>
            {
                return ExecutionResult.Next();
            })
            .Decide(data => data.Value1)
                .Branch((data, outcome) => data.Value1.ToLower() == CValue.ToLower(), level1User1);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants