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);
        }
Exemple #2
0
        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);
        }