/// <exception cref="WorkflowException">A workflow exception occurred. </exception> private async Task <WorkflowActivityOutput> CompleteInternalAsync <TInput>( IWorkflowActivityBase activity, CancellationToken cancellationToken, TInput input) where TInput : WorkflowActivityInput { if (CurrentActivityIds.Contains(activity.Id)) { input = CreateActivityInput(activity, input); WorkflowActivityOutput result; try { result = await activity.CompleteAsync(input, cancellationToken); } catch (Exception exception) { return(HandleCompletionException(exception)); } if (result.Successful) { return(await HandleCompletionSuccess(activity, result)); } else { return(HandleCompletionFailed(result)); } } throw new WorkflowException("The activity to complete could not be found in the current activity list. "); }
/// <exception cref="WorkflowException">A workflow exception occurred. </exception> private TInput CreateActivityInput <TInput>(IWorkflowActivityBase activity, TInput input) where TInput : WorkflowActivityInput { input = input ?? (TInput)Activator.CreateInstance(activity.InputType); input.Instance = this; foreach (var route in activity.Routes) { var outputData = Data.FirstOrDefault(d => d.ActivityId == route.OutputActivityId); if (outputData != null) { var inputProperty = input.GetType().GetRuntimeProperty(route.InputProperty); if (inputProperty != null) { var outputProperty = outputData.Output.GetType().GetRuntimeProperty(route.OutputProperty); if (outputProperty != null) { inputProperty.SetValue(input, outputProperty.GetValue(outputData.Output)); } else { throw new WorkflowException("The output property of the route could not be found on the output data. "); } } else { throw new WorkflowException("The input property of the route could not be found on the input data. "); } } else { throw new WorkflowException("The requested input data for the activity could not be found. "); } } return(input); }
/// <summary>Gets the next activities or null when no condition is available. </summary> /// <param name="activity">The current workflow activity. </param> /// <param name="definition">The workflow definition. </param> /// <returns>The next activities. </returns> /// <exception cref="WorkflowException">No next activities could be found based on the given condition. </exception> /// <exception cref="WorkflowException">More then one activity found for condition but activity is not a ForkActivity. </exception> public IWorkflowActivityBase[] GetNextActivities(IWorkflowActivityBase activity, WorkflowDefinition definition) { if (string.IsNullOrEmpty(NextActivitiesCondition)) { return(null); } var nextActivities = definition.GetOutboundTransitions(activity) .Where(t => t.Condition == NextActivitiesCondition) .Select(t => definition.GetActivityById(t.To)) .ToArray(); if (nextActivities.Length == 0) { throw new WorkflowException( string.Format("No next activities could be found based on the given condition '{0}'. ", NextActivitiesCondition)); } if (nextActivities.Length > 1 && !(activity is ForkActivity)) { throw new WorkflowException( string.Format("More then one activity found for condition '{0}' " + "but activity is not a ForkActivity. ", NextActivitiesCondition)); } return(nextActivities); }
/// <summary>Initializes a new instance of the <see cref="WorkflowRoute"/> class. </summary> /// <typeparam name="TOutputActivity">The type of the output activity's output. </typeparam> /// <typeparam name="TInputActivity">The type of the input activity's input. </typeparam> /// <param name="outputActivity">The output activity. </param> /// <param name="outputPropertyExpression">The output property as expression. </param> /// <param name="inputPropertyExpression">The output property as expression. </param> /// <returns>The <see cref="WorkflowRoute"/>. </returns> public static WorkflowRoute Create <TOutputActivity, TInputActivity>(IWorkflowActivityBase outputActivity, Expression <Func <TOutputActivity, object> > outputPropertyExpression, Expression <Func <TInputActivity, object> > inputPropertyExpression) where TInputActivity : WorkflowActivityInput where TOutputActivity : WorkflowActivityOutput { return(Create(outputActivity.Id, outputPropertyExpression, inputPropertyExpression)); }
/// <exception cref="WorkflowException">Default outgoing transitions of cannot be conditional. </exception> private IWorkflowActivityBase[] GetDefaultNextActivities(IWorkflowActivityBase activity) { var transitions = WorkflowDefinition.GetOutboundTransitions(activity).ToArray(); if (transitions.Any(t => t.IsConditional)) { throw new WorkflowException(string.Format("Default outgoing transitions of ({0}) cannot be conditional. ", activity.Id)); } return(transitions .Select(t => WorkflowDefinition.GetActivityById(t.To)) .ToArray()); }
private void AddActivityResultToData(IWorkflowActivityBase activity, WorkflowActivityOutput result) { var data = Data.SingleOrDefault(d => d.ActivityId == activity.Id); if (data != null) { Data.Remove(data); } Data.Add(new ActivityData { ActivityId = activity.Id, Output = result }); }
/// <summary>Executes the given activity with the given arguments. </summary> /// <exception cref="WorkflowException">A workflow exception occurred. </exception> public async Task <WorkflowActivityOutput> CompleteAsync <TInput>( IWorkflowActivityBase activity, CancellationToken cancellationToken, TInput input) where TInput : WorkflowActivityInput { try { IsRunning = true; return(await CompleteInternalAsync(activity, cancellationToken, input)); } finally { IsRunning = false; RaiseCurrentActivitiesChanged(); } }
private bool HasCurrentActivityBeforeActivity(WorkflowActivityInput input, WorkflowDefinition definition, IWorkflowActivityBase activity, List<WorkflowTransition> checkedTransitions) { var inboundTransitions = definition.GetInboundTransitions(activity); if (inboundTransitions.Any(t => input.Instance.CurrentActivityIds.Contains(t.From))) return true; checkedTransitions.AddRange(inboundTransitions); foreach (var transition in inboundTransitions) { if (HasCurrentActivityBeforeActivity(input, definition, definition.GetActivityById(transition.From), checkedTransitions)) return true; } return false; }
/// <summary>Gets the next activities or null when no condition is available. </summary> /// <param name="activity">The current workflow activity. </param> /// <param name="definition">The workflow definition. </param> /// <returns>The next activities. </returns> /// <exception cref="WorkflowException">No next activities could be found based on the given condition. </exception> /// <exception cref="WorkflowException">More then one activity found for condition but activity is not a ForkActivity. </exception> public IWorkflowActivityBase[] GetNextActivities(IWorkflowActivityBase activity, WorkflowDefinition definition) { if (string.IsNullOrEmpty(NextActivitiesCondition)) return null; var nextActivities = definition.GetOutboundTransitions(activity) .Where(t => t.Condition == NextActivitiesCondition) .Select(t => definition.GetActivityById(t.To)) .ToArray(); if (nextActivities.Length == 0) throw new WorkflowException( string.Format("No next activities could be found based on the given condition '{0}'. ", NextActivitiesCondition)); if (nextActivities.Length > 1 && !(activity is ForkActivity)) throw new WorkflowException( string.Format("More then one activity found for condition '{0}' " + "but activity is not a ForkActivity. ", NextActivitiesCondition)); return nextActivities; }
private bool HasCurrentActivityBeforeActivity(WorkflowActivityInput input, WorkflowDefinition definition, IWorkflowActivityBase activity, List <WorkflowTransition> checkedTransitions) { var inboundTransitions = definition.GetInboundTransitions(activity); if (inboundTransitions.Any(t => input.Instance.CurrentActivityIds.Contains(t.From))) { return(true); } checkedTransitions.AddRange(inboundTransitions); foreach (var transition in inboundTransitions) { if (HasCurrentActivityBeforeActivity(input, definition, definition.GetActivityById(transition.From), checkedTransitions)) { return(true); } } return(false); }
/// <exception cref="WorkflowException">A workflow validation exception occurred. </exception> private void ValidateRoute(IWorkflowActivityBase inputActivity, WorkflowRoute route) { var outputActivity = GetActivityById(route.OutputActivityId); var outputProperty = outputActivity.OutputType.GetRuntimeProperty(route.OutputProperty); if (outputProperty == null) { throw new WorkflowException("The output property of the route could not be found on the output data type. "); } var inputProperty = inputActivity.InputType.GetRuntimeProperty(route.InputProperty); if (inputProperty == null) { throw new WorkflowException("The input property of the route could not be found on the input data type. "); } if (outputProperty.PropertyType != inputProperty.PropertyType) { throw new WorkflowException("The input property and output property types of a route do not match. "); } }
/// <exception cref="WorkflowException">A workflow exception occurred. </exception> private async Task <WorkflowActivityOutput> HandleCompletionSuccess(IWorkflowActivityBase activity, WorkflowActivityOutput result) { AddActivityResultToData(activity, result); var nextActivities = result.GetNextActivities(activity, WorkflowDefinition); if (nextActivities == null) { nextActivities = GetDefaultNextActivities(activity); } if (!(activity is ForkActivity) && nextActivities.Length > 1) { throw new WorkflowException(string.Format("Activity ({0}) has multiple next activities ({1}) " + "but only ForkActivity activities can return multiple activities. ", activity.Id, string.Join(", ", nextActivities.Select(a => a.Id)))); } CurrentActivityIds.Remove(activity.Id); await AddNextActivitiesAsync(activity, nextActivities); return(result); }
/// <exception cref="WorkflowException">A workflow exception occurred. </exception> private async Task AddNextActivitiesAsync(IWorkflowActivityBase activity, IList <IWorkflowActivityBase> nextActivities) { var allowedTransitions = WorkflowDefinition.GetOutboundTransitions(activity); var hasFollowingActivities = allowedTransitions.Length > 0; if (!hasFollowingActivities) { return; } var areNextActivitiesAllowed = nextActivities.All(a => allowedTransitions.Any(t => t.To == a.Id)); if (areNextActivitiesAllowed) { await PrepareNextActivities(nextActivities); } else { throw new WorkflowException( string.Format("Transitions for activities ({0}) produced by activity ({1}) could not be found. ", string.Join(", ", nextActivities.Select(a => a.Id)), activity.Id)); } }
/// <summary>Gets the inbound transitions of a given inputActivity. </summary> /// <param name="activity">The inputActivity. </param> /// <returns>The transitions. </returns> public WorkflowTransition[] GetInboundTransitions(IWorkflowActivityBase activity) { return(Transitions.Where(t => t.To == activity.Id).ToArray()); }
/// <summary>Initializes a new instance of the <see cref="WorkflowRoute"/> class. </summary> /// <param name="outputActivity">The output activity. </param> /// <param name="outputProperty">The output property defined on the output activity's output type. </param> /// <param name="inputProperty">The input property defined on the input activity's input type. </param> public WorkflowRoute(IWorkflowActivityBase outputActivity, string outputProperty, string inputProperty) { OutputActivityId = outputActivity.Id; OutputProperty = outputProperty; InputProperty = inputProperty; }
/// <exception cref="WorkflowException">A workflow validation exception occurred. </exception> private void ValidateRoute(IWorkflowActivityBase inputActivity, WorkflowRoute route) { var outputActivity = GetActivityById(route.OutputActivityId); var outputProperty = outputActivity.OutputType.GetRuntimeProperty(route.OutputProperty); if (outputProperty == null) throw new WorkflowException("The output property of the route could not be found on the output data type. "); var inputProperty = inputActivity.InputType.GetRuntimeProperty(route.InputProperty); if (inputProperty == null) throw new WorkflowException("The input property of the route could not be found on the input data type. "); if (outputProperty.PropertyType != inputProperty.PropertyType) throw new WorkflowException("The input property and output property types of a route do not match. "); }
/// <summary>Gets the inbound transitions of a given inputActivity. </summary> /// <param name="activity">The inputActivity. </param> /// <returns>The transitions. </returns> public WorkflowTransition[] GetInboundTransitions(IWorkflowActivityBase activity) { return Transitions.Where(t => t.To == activity.Id).ToArray(); }
/// <summary>Initializes a new instance of the <see cref="WorkflowTransition"/> class. </summary> public WorkflowTransition(IWorkflowActivityBase from, IWorkflowActivityBase to) { From = from.Id; To = to.Id; }
/// <summary>Executes the given activity with the given arguments. </summary> /// <exception cref="WorkflowException">A workflow exception occurred. </exception> public Task <WorkflowActivityOutput> CompleteAsync(IWorkflowActivityBase activity, CancellationToken cancellationToken) { return(CompleteAsync <WorkflowActivityInput>(activity, cancellationToken, null)); }
/// <summary>Executes the given activity with the given arguments. </summary> /// <exception cref="WorkflowException">A workflow exception occurred. </exception> public Task <WorkflowActivityOutput> CompleteAsync <TInput>(IWorkflowActivityBase activity, TInput input) where TInput : WorkflowActivityInput { return(CompleteAsync(activity, CancellationToken.None, input)); }