private async Task ProcessAsyncActions(AsyncAction[][] actions) { if (actions == null) { throw new ArgumentNullException("Out-of-proc orchestrator schema must have a non-null actions property."); } // Each actionSet represents a particular execution of the orchestration. foreach (AsyncAction[] actionSet in actions) { var tasks = new List <Task>(actions.Length); DurableOrchestrationContext ctx = this.context as DurableOrchestrationContext; // An actionSet represents all actions that were scheduled within that execution. foreach (AsyncAction action in actionSet) { switch (action.ActionType) { case AsyncActionType.CallActivity: tasks.Add(this.context.CallActivityAsync(action.FunctionName, action.Input)); break; case AsyncActionType.CreateTimer: using (var cts = new CancellationTokenSource()) { if (ctx != null) { ctx.ThrowIfInvalidTimerLengthForStorageProvider(action.FireAt); } tasks.Add(this.context.CreateTimer(action.FireAt, cts.Token)); if (action.IsCanceled) { cts.Cancel(); } } break; case AsyncActionType.CallActivityWithRetry: tasks.Add(this.context.CallActivityWithRetryAsync(action.FunctionName, action.RetryOptions, action.Input)); break; case AsyncActionType.CallSubOrchestrator: tasks.Add(this.context.CallSubOrchestratorAsync(action.FunctionName, action.InstanceId, action.Input)); break; case AsyncActionType.CallSubOrchestratorWithRetry: tasks.Add(this.context.CallSubOrchestratorWithRetryAsync(action.FunctionName, action.RetryOptions, action.InstanceId, action.Input)); break; case AsyncActionType.CallEntity: { var entityId = EntityId.GetEntityIdFromSchedulerId(action.InstanceId); tasks.Add(this.context.CallEntityAsync(entityId, action.EntityOperation, action.Input)); break; } case AsyncActionType.SignalEntity: { // We do not add a task because this is 'fire and forget' var entityId = EntityId.GetEntityIdFromSchedulerId(action.InstanceId); this.context.SignalEntity(entityId, action.EntityOperation, action.Input); break; } case AsyncActionType.ScheduledSignalEntity: { // We do not add a task because this is 'fire and forget' var entityId = EntityId.GetEntityIdFromSchedulerId(action.InstanceId); this.context.SignalEntity(entityId, action.FireAt, action.EntityOperation, action.Input); break; } case AsyncActionType.ContinueAsNew: this.context.ContinueAsNew(action.Input); break; case AsyncActionType.WaitForExternalEvent: tasks.Add(this.context.WaitForExternalEvent <object>(action.ExternalEventName)); break; case AsyncActionType.CallHttp: tasks.Add(this.context.CallHttpAsync(action.HttpRequest)); break; default: break; } } if (tasks.Count > 0) { await Task.WhenAny(tasks); } } }
/// <summary> /// Invokes a DF API based on the input action object. /// </summary> /// <param name="action">An OOProc action object representing a DF task.</param> /// <returns>If the API returns a task, the DF task corresponding to the input action. Else, null.</returns> private Task InvokeAPIFromAction(AsyncAction action) { Task fireAndForgetTask = Task.CompletedTask; Task task = null; switch (action.ActionType) { case AsyncActionType.CallActivity: task = this.context.CallActivityAsync(action.FunctionName, action.Input); break; case AsyncActionType.CreateTimer: DurableOrchestrationContext ctx = this.context as DurableOrchestrationContext; using (var cts = new CancellationTokenSource()) { if (ctx != null) { ctx.ThrowIfInvalidTimerLengthForStorageProvider(action.FireAt); } task = this.context.CreateTimer(action.FireAt, cts.Token); if (action.IsCanceled) { cts.Cancel(); } } break; case AsyncActionType.CallActivityWithRetry: task = this.context.CallActivityWithRetryAsync(action.FunctionName, action.RetryOptions, action.Input); break; case AsyncActionType.CallSubOrchestrator: task = this.context.CallSubOrchestratorAsync(action.FunctionName, action.InstanceId, action.Input); break; case AsyncActionType.CallSubOrchestratorWithRetry: task = this.context.CallSubOrchestratorWithRetryAsync(action.FunctionName, action.RetryOptions, action.InstanceId, action.Input); break; case AsyncActionType.CallEntity: { var entityId = EntityId.GetEntityIdFromSchedulerId(action.InstanceId); task = this.context.CallEntityAsync(entityId, action.EntityOperation, action.Input); break; } case AsyncActionType.SignalEntity: { // We do not add a task because this is 'fire and forget' var entityId = EntityId.GetEntityIdFromSchedulerId(action.InstanceId); this.context.SignalEntity(entityId, action.EntityOperation, action.Input); task = fireAndForgetTask; break; } case AsyncActionType.ScheduledSignalEntity: { // We do not add a task because this is 'fire and forget' var entityId = EntityId.GetEntityIdFromSchedulerId(action.InstanceId); this.context.SignalEntity(entityId, action.FireAt, action.EntityOperation, action.Input); task = fireAndForgetTask; break; } case AsyncActionType.ContinueAsNew: this.context.ContinueAsNew(action.Input); task = fireAndForgetTask; break; case AsyncActionType.WaitForExternalEvent: task = this.context.WaitForExternalEvent <object>(action.ExternalEventName); break; case AsyncActionType.CallHttp: task = this.context.CallHttpAsync(action.HttpRequest); break; case AsyncActionType.WhenAll: task = Task.WhenAll(action.CompoundActions.Select(x => this.InvokeAPIFromAction(x))); break; case AsyncActionType.WhenAny: task = Task.WhenAny(action.CompoundActions.Select(x => this.InvokeAPIFromAction(x))); break; default: throw new Exception($"Received an unexpected action type from the out-of-proc function: '${action.ActionType}'."); } return(task); }