ContainerFlow - aceryan-consulting/aceryansoft.codeflow GitHub Wiki

let's start this section by reviewing the required concepts behind ContainerFlow.

ContainerFlow Is a sub class of ActivityFlow and contains a collection of child ActivityFlow. ContainerFlow ca be divided in 3 categories :

  • SequenceContainerFlow (If,Switch,While) : execute contained activities sequentially.
  • IteratorContainerFlow (Foreach,ParallelForeach) : Iterate through a list and execute contained activities (sequentially/in parallel) for each element in the list.
  • ParallelContainerFlow (Parallel) : execute contained activities in parallel.

I don't thing that it is necessary to bother you with basic concepts like (If,Switch,While,Foreach,ParallelForeach),the code for these instructions should be clear and intuitive enough.

A key point when using aceryansoft.codeflow framework is to always remember the feature driven programming design. So always design your processes as a collection of features, loop instructions like (If,Switch,While, etc ...) can only be used to combined functional activities. Activities should only have single functional responsibility.

SequenceContainerFlow

One of our aicodeflow consumer came out with new requirements, he want to :

  • load customers data from different source depending on predefined arguments.
  • clean data multiple times to enhance quality.
  • send an alert to marketing team if SpendingThreshold indicator is under 1 (we are loosing customers spending to others).
  • save results to data base for further analysis.

look how we can use If, Switch, While in aicodeflow.

var aicodeFlow = new CodeFlow();
aicodeFlow.StartNew(cfg => {
	cfg.WithContext(() => new AiContext() { CleanAttempts=3 })
	.UseMiddleware<IExceptionHandlingMiddleware>()  
	.UseActivityFilter<ILogActivityFilter>()  
	.WithServiceResolver(serviceResolver);
})
.Switch((ctx, inputs) => dataSource)
	.Case((val, inputs) => val?.ToString() == "external")
		.Do<ILoadExternalCustomerActivity>()
	.Case((val, inputs) => val?.ToString() == "reuters")
		.Do<ILoadReutersCustomerActivity>()
	.Default()
		.Do<ILoadCustomerActivity>()
.CloseSwitch()
.Do<ILoadCustomerActivity>()  
.Do<ILoadCustomerOrdersActivity>()
.While((ctx, inputs) => ctx.GetValue<int>("CleanAttempts")>0 && ctx.GetCollection<Customer>("Customers").Any(x=>!x.IsCleaned) )
	.CallCodeFlow(FillAndCleanCustomerData)
	.Call((ctx, inputs)=>
	{
		var attempts = ctx.GetValue<int>("CleanAttempts");
		ctx.SetValue<int>("CleanAttempts", attempts - 1);
	})
.Close()            
.Function<ISpendingThresholdFunction>()
	.WithArg<string>("Country", (ctx, inputs) => "France")
	.WithArg<decimal>("AdjustmentValue", (ctx, inputs) => 1.85M)
	.WithResult<decimal>((ctx, inputs, res) => ctx.SetValue<decimal>("SpendingThreshold", res))
.Close()
.If((ctx, inputs) => ctx.GetValue<decimal>("SpendingThreshold") < 1)
	 .Call((ctx, inputs) =>
	 {
		//send alert to the marketing team
	 })
.Close()
.Call((ctx, inputs) =>
 {
	 //save results to data base for further analysis 
 })
.Close();

aicodeFlow.Execute();

IteratorContainerFlow

Another aicodeflow consumer came out with new requirements, he want to :

  • update the SpendingScore of each customer according to new functional rules.

look how we can use Foreach,ParallelForeach in aicodeflow.

 var aicodeFlow = new CodeFlow();
aicodeFlow.StartNew(cfg => {
	cfg.WithContext(() => new AiContext() { CleanAttempts=3 })
	.UseMiddleware<IExceptionHandlingMiddleware>()  
	.UseActivityFilter<ILogActivityFilter>()  
	.WithServiceResolver(serviceResolver);
})
.Switch((ctx, inputs) => dataSource)
	.Case((val, inputs) => val?.ToString() == "external")
		.Do<ILoadExternalCustomerActivity>()
	.Case((val, inputs) => val?.ToString() == "reuters")
		.Do<ILoadReutersCustomerActivity>()
	.Default()
		.Do<ILoadCustomerActivity>()
.CloseSwitch()
.Do<ILoadCustomerActivity>()  
.Do<ILoadCustomerOrdersActivity>()
.While((ctx, inputs) => ctx.GetValue<int>("CleanAttempts")>0 && ctx.GetCollection<Customer>("Customers").Any(x=>!x.IsCleaned) )
	.CallCodeFlow(FillAndCleanCustomerData)
	.Call((ctx, inputs)=>
	{
		var attempts = ctx.GetValue<int>("CleanAttempts");
		ctx.SetValue<int>("CleanAttempts", attempts - 1);
	})
.Close()
.ForEach((ctx, inputs) => ctx.GetCollection<Customer>("Customers").OfType<object>().ToList(), packetSize: 10) // packetSize is optional and no iteration split by package logic is applied when not specified
	//.AsSequence() you can use this to execute contained activities in sequence 
	.AsParallel() // execute contained activities in parallel and by packet of (packetSize = 10)
		 .Call((ctx, inputs) =>
		 {
			 //update the SpendingScore of the current customer = inputs[0] 
		 })
	.Close()
.CloseForEach()
.Function<ISpendingThresholdFunction>()
	.WithArg<string>("Country", (ctx, inputs) => "France")
	.WithArg<decimal>("AdjustmentValue", (ctx, inputs) => 1.85M)
	.WithResult<decimal>((ctx, inputs, res) => ctx.SetValue<decimal>("SpendingThreshold", res))
.Close()
.If((ctx, inputs) => ctx.GetValue<decimal>("SpendingThreshold") < 1)
	 .Call((ctx, inputs) =>
	 {
		//send alert to the marketing team
	 })
.Close()
.Call((ctx, inputs) =>
 {
	 //save results to data base for further analysis 
 })
.Close();

aicodeFlow.Execute();

Please remember that when using Foreach loop, the inputs parameters passed to your activities or functions are filled with these rules :

  • inputs[0 ... n] will contains loop values.
  • inputs[n+1 ... n+p] will contains function parameter values.

For more explanation, look this sample codeflow with nested foreach loops.

public class ForeachCodeFlow
{
	public void Run()
	{ 
		var foreachcodeFlow = new CodeFlow();
		foreachcodeFlow.StartNew(cfg => cfg.WithContext(() => new ForeachContext()))
		.ForEach((ctx, inputs) => Enumerable.Range(0,2).OfType<object>().ToList() ) 
			.AsSequence()
			   .ForEach((ctx, inputs) => Enumerable.Range(1,1).OfType<object>().ToList())
					.AsParallel()
						 .Function((ctx, inputs) =>
						 {
							 // for the first iteration inputs[0]=0 , inputs[1]=1 index on the inner iteration, inputs[2]= 7 SomeArg value
							 return (int)inputs[0] + (int)inputs[1] + (int)inputs[2];
						 }).WithArg<int>("SomeArg", (ctx, inputs) => 7)
						 .WithResult<int>((ctx, inputs, res) => ctx.As<ForeachContext>().Results.Add(res))
					.Close()
				.CloseForEach()
			.Close()
		.CloseForEach() 
		.Close();

		foreachcodeFlow.Execute();
	}         
}

public class ForeachContext : CodeFlowContext
{
	public ConcurrentBag<int> Results { get; set; } = new ConcurrentBag<int>();

	public ForeachContext()
	{
		ContextProperties["Results"] = new ContextProperty(() => Results, (obj) => { Results = (ConcurrentBag<int>)obj; });
	}
}

Continue reading: RestorePoint

⚠️ **GitHub.com Fallback** ⚠️