workflow-core icon indicating copy to clipboard operation
workflow-core copied to clipboard

Passing context.item from one step as output to next step as intut?

Open andreasfrei opened this issue 4 years ago • 4 comments

Hi,

is there a way to pass in a foreach loop a data object from one step to the next?

For Example in sample below I would like to passe from first step "Compress" the step.outFile to the next step "Image" as context.Item. As .Output does not have a context my idea does not work. Is there another way to do this?

              .ForEach(data => data.ProcessingFiles)
                .Do(x =>
                       x.StartWith<CompressAction>()
                            .Name("myCompressInLoop")
                            .Input((step, data, context) =>
                            {
                                step.inFile = context.Item;
                            })
                            .Output((step, data) =>
                            {
                                context.Item = step.outFile; 
                            })

                        .Then<ImageAction>()
                            .Name("myImageInLoop")
                            .Input((step, data, context) =>
                            {
                                step.inFile = context.Item;
                            })
                            .Output((step, data) =>
                            {
                                data.AddProcessingFile = step.outFile;
                            })
                  )

andreasfrei avatar Dec 18 '20 16:12 andreasfrei

Why don't you just map the compress output to a value on the workflow data object and then map to input of the next step to that field?

danielgerlag avatar Dec 20 '20 16:12 danielgerlag

I can keep a list of the current loop steps in the wf data object, will give this a try.

In the scenario where the workflow engine is running on multiple instances how is wf data object synchronized between them? Is a change in the wf data object transactional?

andreasfrei avatar Dec 21 '20 06:12 andreasfrei

I do not think that is transactional.

Here is a workflow with ForEach,

        builder
            .ForEach(data => new List<int> { 1, 2, 3, 4 })
                .Do(x => x
                    .StartWith<DoForeach>()
                        .Input(step => step.Item, (Data data, IStepExecutionContext context) => (int)context.Item)
                        .Output(w => w.Id, step => step.ItemOut)
                    .Then<DoForeach>()
                        .Input(s => s.Item, (step, context) => step.Id)
                )
                .OnError(WorkflowErrorHandling.Terminate);

and the Data class is defined as

    public class Data
    {
        public int Id { get; set; }
    }

The DoForeach will take Item as input, and doubles it as output ItemOut. The execution result shows

Starting workflow...
78e3e226-8e0f-456c-86fd-eea655532e62
1
2
3
4
8
8
8
8

So there I think these parallel steps takes a shared instance of Data. I think the only way is to use a Dictionary and map each loop to a cell in the dictionary, like this

    public class Data
    {
        public IDictionary<int, int> Id { get; set; } = new Dictionary<int, int>();
    }

        builder
            .ForEach(data => new List<int> { 1, 2, 3, 4 })
                .Do(x => x
                    .StartWith<DoForeach>()
                        .Input(step => step.Item, (Data data, IStepExecutionContext context) => (int)context.Item)
                        .Output((s, w) => w.Id[s.Item] = s.ItemOut)
                    .Then<DoForeach>()
                        .Input(s => s.Item, (step, context) => step.Id[(int)context.Item])
                )
                .OnError(WorkflowErrorHandling.Terminate);

I wonder if there is better way of handling this pattern.

xfoxfu avatar Sep 21 '23 09:09 xfoxfu