async Task <object> ContinueState(SuspendState state, WorkflowInstanceState workflowstate, WorkableLogger tasklogger, IDictionary <string, object> variables, CancellationToken token) { object lastresult = null; if (state.Subflow != null) { WorkflowIdentifier workflow = workflowstate.Workflow; workflowstate.Workflow = state.Subflow.Workflow; lastresult = await ContinueState(state.Subflow, workflowstate, tasklogger, variables, token); workflowstate.Workflow = workflow; } else { if (variables != null) { foreach (KeyValuePair <string, object> entry in variables) { state.Variables[entry.Key] = entry.Value.DetermineValue(state.Variables); } } } InstanceTransition transition = await EvaluateTransitions(state.Node, tasklogger, state.Variables, state.Node.Transitions, token); if (transition == null) { tasklogger.Warning("Suspend node has no transition defined for current state. Workflow ends by default."); return(lastresult); } return(await Execute(workflowstate, token, transition.Target, lastresult)); }
async Task <InstanceTransition> EvaluateTransitions(IInstanceNode current, WorkableLogger tasklogger, IVariableProvider variableprovider, List <InstanceTransition> transitions, CancellationToken token) { if (transitions == null) { return(null); } InstanceTransition transition = null; foreach (InstanceTransition conditionaltransition in transitions.Where(c => c.Condition != null)) { if (await conditionaltransition.Condition.ExecuteAsync <bool>(variableprovider, token)) { transition = conditionaltransition; break; } } if (transition == null) { InstanceTransition[] defaulttransitions = transitions.Where(t => t.Condition == null).ToArray(); if (defaulttransitions.Length > 1) { throw new InvalidOperationException($"More than one default transition defined for '{current.NodeName}'."); } transition = defaulttransitions.FirstOrDefault(); } if (transition?.Log != null) { try { tasklogger.Info(await transition.Log.ExecuteAsync <string>(variableprovider, token)); } catch (Exception e) { tasklogger.Error($"Error executing transition log of '{current.NodeName}'->'{transition.Target?.NodeName}'", e); throw; } } return(transition); }
async Task <object> Execute(WorkflowInstanceState state, CancellationToken token, IInstanceNode current, object lastresult = null) { while (current != null) { try { if (state.Profiling) { Stopwatch stopwatch = Stopwatch.StartNew(); lastresult = await current.Execute(state, token); state.Logger.Performance(state.Workflow, current.NodeId, current.NodeName, stopwatch.Elapsed); } else { lastresult = await current.Execute(state, token); } if (token.IsCancellationRequested) { throw new TaskCanceledException(); } // used for workflow suspension if (lastresult is SuspendState) { return(lastresult); } } catch (Exception e) { state.Logger.Warning($"Error while executing node '{current.NodeName}'", e.Message); InstanceTransition next = await EvaluateTransitions(current, state.Logger, new VariableProvider(state.Variables, new Variable("error", e)), current.ErrorTransitions, token); if (next == null) { throw; } current = next.Target; continue; } try { InstanceTransition transition; if (lastresult is LoopCommand) { if (current.LoopTransitions.Count == 0) { throw new InvalidOperationException("Iterator node without any loop transitions detected."); } transition = await EvaluateTransitions(current, state.Logger, state.Variables, current.LoopTransitions, token); current = transition?.Target ?? current; } else { transition = await EvaluateTransitions(current, state.Logger, state.Variables, current.Transitions, token); current = transition?.Target; } } catch (Exception e) { state.Logger.Warning($"Error while evaluating transitions of node '{current?.NodeName}'", e.Message); InstanceTransition next = await EvaluateTransitions(current, state.Logger, new VariableProvider(state.Variables, new Variable("error", e)), current?.ErrorTransitions, token); if (next == null) { throw; } current = next.Target; } } return(lastresult); }