/// <summary> /// Starts the target workflow that returns <typeparamref name="TResult"/>, passing any specified arguments. /// </summary> /// <typeparam name="TResult">The workflow result type.</typeparam> /// <param name="args">The arguments to be passed to the workflow.</param> /// <returns>The <see cref="ChildWorkflowFuture{T}"/> with the <see cref="ChildWorkflowFuture{T}.GetAsync"/> that can be used to retrieve the workfow result.</returns> /// <exception cref="InvalidOperationException">Thrown when attempting to start a future stub more than once.</exception> /// <remarks> /// <para> /// You must take care to pass parameters that are compatible with the target workflow parameters. /// These are checked at runtime but not while compiling. /// </para> /// <note> /// Any given <see cref="ChildWorkflowStub{TWorkflowInterface}"/> may only be executed once. /// </note> /// </remarks> public async Task <ChildWorkflowFuture <TResult> > StartAsync <TResult>(params object[] args) { await SyncContext.Clear; Covenant.Requires <ArgumentNullException>(parentWorkflow != null, nameof(parentWorkflow)); parentWorkflow.SetStackTrace(); if (hasStarted) { throw new InvalidOperationException("Cannot start a future stub more than once."); } var parameters = targetMethod.GetParameters(); if (parameters.Length != args.Length) { throw new ArgumentException($"Invalid number of parameters: [{parameters.Length}] expected but [{args.Length}] were passed.", nameof(parameters)); } hasStarted = true; // Cast the input parameters to the target types so that developers won't need to expicitly // cast things like integers into longs, floats into doubles, etc. for (int i = 0; i < args.Length; i++) { args[i] = TemporalHelper.ConvertArg(parameters[i].ParameterType, args[i]); } // Start the child workflow and then construct and return the future. var client = parentWorkflow.Client; var execution = await client.StartChildWorkflowAsync(parentWorkflow, workflowTypeName, TemporalHelper.ArgsToBytes(client.DataConverter, args), options); // Initialize the type-safe stub property such that developers can call // any query or signal methods. Stub = StubManager.NewChildWorkflowStub <TWorkflowInterface>(client, parentWorkflow, workflowTypeName, execution); // Create and return the future. var resultType = targetMethod.ReturnType; if (resultType == typeof(Task)) { throw new ArgumentException($"Workflow method [{nameof(TWorkflowInterface)}.{targetMethod.Name}()] does not return [void].", nameof(TWorkflowInterface)); } resultType = resultType.GenericTypeArguments.First(); if (!resultType.IsAssignableFrom(typeof(TResult))) { throw new ArgumentException($"Workflow method [{nameof(TWorkflowInterface)}.{targetMethod.Name}()] returns [{resultType.FullName}] which is not compatible with [{nameof(TResult)}].", nameof(TWorkflowInterface)); } return(new ChildWorkflowFuture <TResult>(parentWorkflow, execution)); }
/// <summary> /// Starts the target workflow that returns <c>void</c>, passing any specified arguments. /// </summary> /// <param name="args">The arguments to be passed to the workflow.</param> /// <returns>The <see cref="ChildWorkflowFuture{T}"/> with the <see cref="ChildWorkflowFuture{T}.GetAsync"/> than can be used to retrieve the workfow result.</returns> /// <exception cref="InvalidOperationException">Thrown when attempting to start a future stub more than once.</exception> /// <remarks> /// <para> /// You must take care to pass parameters that are compatible with the target workflow parameters. /// These are checked at runtime but not while compiling. /// </para> /// <note> /// Any given <see cref="ChildWorkflowStub{TWorkflowInterface}"/> may only be executed once. /// </note> /// </remarks> public async Task <ChildWorkflowFuture> StartAsync(params object[] args) { await SyncContext.ClearAsync; Covenant.Requires <ArgumentNullException>(parentWorkflow != null, nameof(parentWorkflow)); parentWorkflow.SetStackTrace(); if (hasStarted) { throw new InvalidOperationException("Cannot start a future stub more than once."); } var parameters = targetMethod.GetParameters(); if (parameters.Length != args.Length) { throw new ArgumentException($"Invalid number of parameters: [{parameters.Length}] expected but [{args.Length}] were passed.", nameof(parameters)); } hasStarted = true; // Cast the input parameters to the target types so that developers won't need to expicitly // cast things like integers into longs, floats into doubles, etc. for (int i = 0; i < args.Length; i++) { args[i] = TypeDescriptor.GetConverter(parameters[i].ParameterType).ConvertTo(args[i], parameters[i].ParameterType); } // Start the child workflow and then construct the future. var client = parentWorkflow.Client; var execution = await client.StartChildWorkflowAsync(parentWorkflow, workflowTypeName, client.DataConverter.ToData(args), options); // Initialize the type-safe stub property such that developers can call // any query or signal methods. Stub = StubManager.NewChildWorkflowStub <TWorkflowInterface>(client, parentWorkflow, workflowTypeName, execution); // Create and return the future. return(new ChildWorkflowFuture(parentWorkflow, execution)); }