/// <summary> /// Starts the target activity that returns <typeparamref name="TResult"/>, passing the specified arguments. /// </summary> /// <typeparam name="TResult">The activity result type.</typeparam> /// <param name="args">The arguments to be passed to the activity.</param> /// <returns>The <see cref="IAsyncFuture{T}"/> with the <see cref="IAsyncFuture{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 activity parameters. /// These are checked at runtime but not while compiling. /// </para> /// <note> /// Any given <see cref="ActivityFutureStub{TActivityInterface}"/> may only be executed once. /// </note> /// </remarks> public async Task <IAsyncFuture <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."); } hasStarted = true; // Start the activity. var client = parentWorkflow.Client; var dataConverter = client.DataConverter; var activityId = parentWorkflow.GetNextActivityId(); var reply = await parentWorkflow.ExecuteNonParallel( async() => { return((ActivityStartReply)await client.CallProxyAsync( new ActivityStartRequest() { ContextId = parentWorkflow.ContextId, ActivityId = activityId, Activity = activityTypeName, Args = CadenceHelper.ArgsToBytes(dataConverter, args), Options = options.ToInternal(), Domain = options.Domain })); }); reply.ThrowOnError(); parentWorkflow.UpdateReplay(reply); // Create and return the future. return(new AsyncFuture <TResult>(parentWorkflow, activityId)); }
/// <summary> /// Starts the target activity that returns <typeparamref name="TActivityInterface"/>, passing the specified arguments. /// </summary> /// <typeparam name="TResult">The activity result type.</typeparam> /// <param name="args">The arguments to be passed to the activity.</param> /// <returns>The <see cref="IAsyncFuture{T}"/> with the <see cref="IAsyncFuture{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 activity parameters. /// These are checked at runtime but not while compiling. /// </para> /// <note> /// Any given <see cref="ActivityFutureStub{TActivityInterface}"/> may only be executed once. /// </note> /// </remarks> public async Task <IAsyncFuture <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] = CadenceHelper.ConvertArg(parameters[i].ParameterType, args[i]); } // Validate the return type. var resultType = targetMethod.ReturnType; if (resultType == typeof(Task)) { throw new ArgumentException($"Activity method [{nameof(TActivityInterface)}.{targetMethod.Name}()] does not return [void].", nameof(TActivityInterface)); } resultType = resultType.GenericTypeArguments.First(); if (!resultType.IsAssignableFrom(typeof(TResult))) { throw new ArgumentException($"Activity method [{nameof(TActivityInterface)}.{targetMethod.Name}()] returns [{resultType.FullName}] which is not compatible with [{nameof(TResult)}].", nameof(TActivityInterface)); } // Start the activity. var dataConverter = client.DataConverter; var activityId = parentWorkflow.GetNextActivityId(); var reply = await parentWorkflow.ExecuteNonParallel( async() => { return((ActivityStartReply)await client.CallProxyAsync( new ActivityStartRequest() { ContextId = parentWorkflow.ContextId, ActivityId = activityId, Activity = activityTypeName, Args = CadenceHelper.ArgsToBytes(dataConverter, args), Options = options.ToInternal(), Domain = options.Domain })); }); reply.ThrowOnError(); parentWorkflow.UpdateReplay(reply); // Create and return the future. return(new AsyncFuture <TResult>(parentWorkflow, activityId)); }