public virtual async Task <RunWorkflowResult> RunWorkflowAsync( IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string?activityId = default, object?input = default, CancellationToken cancellationToken = default) { using var loggingScope = _logger.BeginScope(new Dictionary <string, object> { ["WorkflowInstanceId"] = workflowInstance.Id }); using var workflowExecutionScope = _serviceScopeFactory.CreateScope(); var workflowExecutionContext = new WorkflowExecutionContext(workflowExecutionScope.ServiceProvider, workflowBlueprint, workflowInstance, input); if (!string.IsNullOrWhiteSpace(workflowInstance.ContextId)) { var loadContext = new LoadWorkflowContext(workflowExecutionContext); workflowExecutionContext.WorkflowContext = await _workflowContextManager.LoadContext(loadContext, cancellationToken); } // If the workflow instance has a CurrentActivity, it means the workflow instance is being retried. var currentActivity = workflowInstance.CurrentActivity; if (currentActivity != null) { activityId = currentActivity.ActivityId; } var activity = activityId != null?workflowBlueprint.GetActivity(activityId) : default; // Give application a chance to prevent workflow from executing. var validateWorkflowExecution = new ValidateWorkflowExecution(workflowExecutionContext, activity); await _mediator.Publish(validateWorkflowExecution, cancellationToken); if (!validateWorkflowExecution.CanExecuteWorkflow) { _logger.LogInformation("Workflow execution prevented for workflow {WorkflowInstanceId}", workflowInstance.Id); return(new RunWorkflowResult(workflowInstance, activityId, false)); } await _mediator.Publish(new WorkflowExecuting(workflowExecutionContext), cancellationToken); RunWorkflowResult runWorkflowResult; switch (workflowExecutionContext.Status) { case WorkflowStatus.Idle: runWorkflowResult = await BeginWorkflow(workflowExecutionContext, activity, cancellationToken); if (!runWorkflowResult.Executed) { _logger.LogDebug("Workflow {WorkflowInstanceId} cannot begin from an idle state (perhaps it needs a specific input)", workflowInstance.Id); return(runWorkflowResult); } break; case WorkflowStatus.Running: await RunWorkflowAsync(workflowExecutionContext, cancellationToken); runWorkflowResult = new RunWorkflowResult(workflowInstance, activityId, true); break; case WorkflowStatus.Suspended: runWorkflowResult = await ResumeWorkflowAsync(workflowExecutionContext, activity !, cancellationToken); if (!runWorkflowResult.Executed) { _logger.LogDebug("Workflow {WorkflowInstanceId} cannot be resumed from a suspended state (perhaps it needs a specific input)", workflowInstance.Id); return(runWorkflowResult); } break; default: throw new ArgumentOutOfRangeException(); } await _mediator.Publish(new WorkflowExecuted(workflowExecutionContext), cancellationToken); var statusEvent = workflowExecutionContext.Status switch { WorkflowStatus.Cancelled => new WorkflowCancelled(workflowExecutionContext), WorkflowStatus.Finished => new WorkflowCompleted(workflowExecutionContext), WorkflowStatus.Faulted => new WorkflowFaulted(workflowExecutionContext), WorkflowStatus.Suspended => new WorkflowSuspended(workflowExecutionContext), _ => default(INotification) }; if (statusEvent != null) { _logger.LogTrace("Publishing a status event of type {EventType} for workflow {WorkflowInstanceId}", statusEvent.GetType().Name, workflowInstance.Id); await _mediator.Publish(statusEvent, cancellationToken); } await _mediator.Publish(new WorkflowExecutionFinished(workflowExecutionContext), cancellationToken); return(runWorkflowResult); }
protected virtual async Task <RunWorkflowResult> RunWorkflowInternalAsync(WorkflowExecutionContext workflowExecutionContext, string?activityId = default, CancellationToken cancellationToken = default) { var workflowInstance = workflowExecutionContext.WorkflowInstance; if (!string.IsNullOrWhiteSpace(workflowInstance.ContextId)) { var loadContext = new LoadWorkflowContext(workflowExecutionContext); workflowExecutionContext.WorkflowContext = await _workflowContextManager.LoadContext(loadContext, cancellationToken); } // If the workflow instance has a CurrentActivity, it means the workflow instance is being retried. var currentActivity = workflowInstance.CurrentActivity; if (activityId == null && currentActivity != null) { activityId = currentActivity.ActivityId; } var workflowBlueprint = workflowExecutionContext.WorkflowBlueprint; var activity = activityId != null?workflowBlueprint.GetActivity(activityId) : default; // Give application a chance to prevent workflow from executing. var validateWorkflowExecution = new ValidateWorkflowExecution(workflowExecutionContext, activity); await _mediator.Publish(validateWorkflowExecution, cancellationToken); if (!validateWorkflowExecution.CanExecuteWorkflow) { _logger.LogInformation("Workflow execution prevented for workflow {WorkflowInstanceId}", workflowInstance.Id); return(new RunWorkflowResult(workflowInstance, activityId, null, false)); } await _mediator.Publish(new WorkflowExecuting(workflowExecutionContext), cancellationToken); RunWorkflowResult runWorkflowResult; switch (workflowExecutionContext.Status) { case WorkflowStatus.Idle: runWorkflowResult = await BeginWorkflow(workflowExecutionContext, activity, cancellationToken); if (!runWorkflowResult.Executed) { if (workflowInstance.WorkflowStatus != WorkflowStatus.Faulted) { _logger.LogDebug("Workflow {WorkflowInstanceId} cannot begin from an idle state (perhaps it needs a specific input)", workflowInstance.Id); return(runWorkflowResult); } } break; case WorkflowStatus.Running: runWorkflowResult = await RunWorkflowAsync(workflowExecutionContext, activity, cancellationToken); break; case WorkflowStatus.Suspended: runWorkflowResult = await ResumeWorkflowAsync(workflowExecutionContext, activity !, cancellationToken); if (!runWorkflowResult.Executed) { _logger.LogDebug("Workflow {WorkflowInstanceId} cannot be resumed from a suspended state (perhaps it needs a specific input)", workflowInstance.Id); return(runWorkflowResult); } break; default: throw new ArgumentOutOfRangeException(); } await _mediator.Publish(new WorkflowExecuted(workflowExecutionContext), cancellationToken); var statusEvents = workflowExecutionContext.Status switch { WorkflowStatus.Cancelled => new INotification[] { new WorkflowCancelled(workflowExecutionContext), new WorkflowInstanceCancelled(workflowInstance) }, WorkflowStatus.Finished => new INotification[] { new WorkflowCompleted(workflowExecutionContext) }, WorkflowStatus.Faulted => new INotification[] { new WorkflowFaulted(workflowExecutionContext) }, WorkflowStatus.Suspended => new INotification[] { new WorkflowSuspended(workflowExecutionContext) }, _ => Array.Empty <INotification>() }; foreach (var statusEvent in statusEvents) { await _mediator.Publish(statusEvent, cancellationToken); } await _mediator.Publish(new WorkflowExecutionFinished(workflowExecutionContext), cancellationToken); return(runWorkflowResult); }