async void CompleteTaskAsync(String taskToken) { RespondActivityTaskCompletedRequest request = new RespondActivityTaskCompletedRequest() { TaskToken = taskToken }; RespondActivityTaskCompletedResponse response = await this._swfClient.RespondActivityTaskCompletedAsync(request); Logger.LogMessage("{0} Activity task completed.", this.ActivityType); }
/// <summary> /// Initiates the asynchronous execution of the RespondActivityTaskCompleted operation. /// <seealso cref="Amazon.SimpleWorkflow.AmazonSimpleWorkflow.RespondActivityTaskCompleted"/> /// </summary> /// /// <param name="respondActivityTaskCompletedRequest">Container for the necessary parameters to execute the RespondActivityTaskCompleted /// operation on AmazonSimpleWorkflow.</param> /// <param name="callback">An AsyncCallback delegate that is invoked when the operation completes.</param> /// <param name="state">A user-defined state object that is passed to the callback procedure. Retrieve this object from within the callback /// procedure using the AsyncState property.</param> public IAsyncResult BeginRespondActivityTaskCompleted(RespondActivityTaskCompletedRequest respondActivityTaskCompletedRequest, AsyncCallback callback, object state) { return invokeRespondActivityTaskCompleted(respondActivityTaskCompletedRequest, callback, state, false); }
IAsyncResult invokeRespondActivityTaskCompleted(RespondActivityTaskCompletedRequest respondActivityTaskCompletedRequest, AsyncCallback callback, object state, bool synchronized) { IRequest irequest = new RespondActivityTaskCompletedRequestMarshaller().Marshall(respondActivityTaskCompletedRequest); var unmarshaller = RespondActivityTaskCompletedResponseUnmarshaller.GetInstance(); AsyncResult result = new AsyncResult(irequest, callback, state, synchronized, signer, unmarshaller); Invoke(result); return result; }
/// <summary> /// <para> Used by workers to tell the service that the ActivityTask identified by the <c>taskToken</c> completed successfully with a /// <c>result</c> (if provided). </para> <para> The <c>result</c> appears in the <c>ActivityTaskCompleted</c> event in the workflow history. /// </para> <para><b>IMPORTANT:</b> If the requested task does not complete successfully, use RespondActivityTaskFailed instead. If the worker /// finds that the task is canceled through the canceled flag returned by RecordActivityTaskHeartbeat, it should cancel the task, clean up and /// then call RespondActivityTaskCanceled. </para> /// </summary> /// /// <param name="respondActivityTaskCompletedRequest">Container for the necessary parameters to execute the RespondActivityTaskCompleted service /// method on AmazonSimpleWorkflow.</param> /// /// <exception cref="OperationNotPermittedException"/> /// <exception cref="UnknownResourceException"/> public RespondActivityTaskCompletedResponse RespondActivityTaskCompleted(RespondActivityTaskCompletedRequest respondActivityTaskCompletedRequest) { IAsyncResult asyncResult = invokeRespondActivityTaskCompleted(respondActivityTaskCompletedRequest, null, null, true); return EndRespondActivityTaskCompleted(asyncResult); }
/// <summary> /// <para> Used by workers to tell the service that the ActivityTask identified by the <c>taskToken</c> completed successfully with a /// <c>result</c> (if provided). The <c>result</c> appears in the <c>ActivityTaskCompleted</c> event in the workflow history. </para> /// <para><b>IMPORTANT:</b> If the requested task does not complete successfully, use RespondActivityTaskFailed instead. If the worker finds /// that the task is canceled through the canceled flag returned by RecordActivityTaskHeartbeat, it should cancel the task, clean up and then /// call RespondActivityTaskCanceled. </para> <para> A task is considered open from the time that it is scheduled until it is closed. Therefore /// a task is reported as open while a worker is processing it. A task is closed after it has been specified in a call to /// RespondActivityTaskCompleted, RespondActivityTaskCanceled, RespondActivityTaskFailed, or the task has <a href="http://docs.aws.amazon.com/amazonswf/latest/developerguide/swf-dg-basic.html#swf-dev-timeout-types">timed out</a> . /// </para> <para> <b>Access Control</b> </para> <para>You can use IAM policies to control this action's access to Amazon SWF resources as /// follows:</para> /// <ul> /// <li>Use a <c>Resource</c> element with the domain name to limit the action to only specified domains.</li> /// <li>Use an <c>Action</c> element to allow or deny permission to call this action.</li> /// <li>You cannot use an IAM policy to constrain this action's parameters.</li> /// /// </ul> /// <para>If the caller does not have sufficient permissions to invoke the action, or the parameter values fall outside the specified /// constraints, the action fails by throwing <c>OperationNotPermitted</c> . For details and example IAM policies, see <a href="http://docs.aws.amazon.com/amazonswf/latest/developerguide/swf-dev-iam.html">Using IAM to Manage Access to Amazon SWF Workflows</a> /// .</para> /// </summary> /// /// <param name="respondActivityTaskCompletedRequest">Container for the necessary parameters to execute the RespondActivityTaskCompleted service /// method on AmazonSimpleWorkflow.</param> /// /// <exception cref="T:Amazon.SimpleWorkflow.Model.OperationNotPermittedException" /> /// <exception cref="T:Amazon.SimpleWorkflow.Model.UnknownResourceException" /> /// <param name="cancellationToken"> /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// </param> public Task<RespondActivityTaskCompletedResponse> RespondActivityTaskCompletedAsync(RespondActivityTaskCompletedRequest respondActivityTaskCompletedRequest, CancellationToken cancellationToken = default(CancellationToken)) { var marshaller = new RespondActivityTaskCompletedRequestMarshaller(); var unmarshaller = RespondActivityTaskCompletedResponseUnmarshaller.GetInstance(); return Invoke<IRequest, RespondActivityTaskCompletedRequest, RespondActivityTaskCompletedResponse>(respondActivityTaskCompletedRequest, marshaller, unmarshaller, signer, cancellationToken); }
internal RespondActivityTaskCompletedResponse RespondActivityTaskCompleted(RespondActivityTaskCompletedRequest request) { var task = RespondActivityTaskCompletedAsync(request); try { return task.Result; } catch(AggregateException e) { ExceptionDispatchInfo.Capture(e.InnerException).Throw(); return null; } }
/// <summary> /// Initiates the asynchronous execution of the RespondActivityTaskCompleted operation. /// <seealso cref="Amazon.SimpleWorkflow.IAmazonSimpleWorkflow"/> /// </summary> /// /// <param name="request">Container for the necessary parameters to execute the RespondActivityTaskCompleted operation.</param> /// <param name="cancellationToken"> /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// </param> /// <returns>The task object representing the asynchronous operation.</returns> public Task<RespondActivityTaskCompletedResponse> RespondActivityTaskCompletedAsync(RespondActivityTaskCompletedRequest request, System.Threading.CancellationToken cancellationToken = default(CancellationToken)) { var marshaller = new RespondActivityTaskCompletedRequestMarshaller(); var unmarshaller = RespondActivityTaskCompletedResponseUnmarshaller.Instance; return InvokeAsync<RespondActivityTaskCompletedRequest,RespondActivityTaskCompletedResponse>(request, marshaller, unmarshaller, cancellationToken); }
/// <summary> /// Used by workers to tell the service that the <a>ActivityTask</a> identified by the /// <code>taskToken</code> completed successfully with a <code>result</code> (if provided). /// The <code>result</code> appears in the <code>ActivityTaskCompleted</code> event in /// the workflow history. /// /// <important> If the requested task does not complete successfully, use <a>RespondActivityTaskFailed</a> /// instead. If the worker finds that the task is canceled through the <code>canceled</code> /// flag returned by <a>RecordActivityTaskHeartbeat</a>, it should cancel the task, clean /// up and then call <a>RespondActivityTaskCanceled</a>. </important> /// <para> /// A task is considered open from the time that it is scheduled until it is closed. /// Therefore a task is reported as open while a worker is processing it. A task is closed /// after it has been specified in a call to RespondActivityTaskCompleted, <a>RespondActivityTaskCanceled</a>, /// <a>RespondActivityTaskFailed</a>, or the task has <a href="http://docs.aws.amazon.com/amazonswf/latest/developerguide/swf-dg-basic.html#swf-dev-timeout-types">timed /// out</a>. /// </para> /// /// <para> /// <b>Access Control</b> /// </para> /// /// <para> /// You can use IAM policies to control this action's access to Amazon SWF resources as /// follows: /// </para> /// <ul> <li>Use a <code>Resource</code> element with the domain name to limit the action /// to only specified domains.</li> <li>Use an <code>Action</code> element to allow or /// deny permission to call this action.</li> <li>You cannot use an IAM policy to constrain /// this action's parameters.</li> </ul> /// <para> /// If the caller does not have sufficient permissions to invoke the action, or the parameter /// values fall outside the specified constraints, the action fails by throwing <code>OperationNotPermitted</code>. /// For details and example IAM policies, see <a href="http://docs.aws.amazon.com/amazonswf/latest/developerguide/swf-dev-iam.html">Using /// IAM to Manage Access to Amazon SWF Workflows</a>. /// </para> /// </summary> /// <param name="request">Container for the necessary parameters to execute the RespondActivityTaskCompleted service method.</param> /// /// <returns>The response from the RespondActivityTaskCompleted service method, as returned by SimpleWorkflow.</returns> /// <exception cref="OperationNotPermittedException"> /// Returned when the caller does not have sufficient permissions to invoke the action. /// </exception> /// <exception cref="UnknownResourceException"> /// Returned when the named resource cannot be found with in the scope of this operation /// (region or domain). This could happen if the named resource was never created or is /// no longer available for this operation. /// </exception> public RespondActivityTaskCompletedResponse RespondActivityTaskCompleted(RespondActivityTaskCompletedRequest request) { var marshaller = new RespondActivityTaskCompletedRequestMarshaller(); var unmarshaller = RespondActivityTaskCompletedResponseUnmarshaller.Instance; return Invoke<RespondActivityTaskCompletedRequest,RespondActivityTaskCompletedResponse>(request, marshaller, unmarshaller); }
/// <summary> /// Initiates the asynchronous execution of the RespondActivityTaskCompleted operation. /// </summary> /// /// <param name="request">Container for the necessary parameters to execute the RespondActivityTaskCompleted operation on AmazonSimpleWorkflowClient.</param> /// <param name="callback">An AsyncCallback delegate that is invoked when the operation completes.</param> /// <param name="state">A user-defined state object that is passed to the callback procedure. Retrieve this object from within the callback /// procedure using the AsyncState property.</param> /// /// <returns>An IAsyncResult that can be used to poll or wait for results, or both; this value is also needed when invoking EndRespondActivityTaskCompleted /// operation.</returns> public IAsyncResult BeginRespondActivityTaskCompleted(RespondActivityTaskCompletedRequest request, AsyncCallback callback, object state) { var marshaller = new RespondActivityTaskCompletedRequestMarshaller(); var unmarshaller = RespondActivityTaskCompletedResponseUnmarshaller.Instance; return BeginInvoke<RespondActivityTaskCompletedRequest>(request, marshaller, unmarshaller, callback, state); }
/// <summary> /// <para> Used by workers to tell the service that the ActivityTask identified by the <c>taskToken</c> completed successfully with a /// <c>result</c> (if provided). The <c>result</c> appears in the <c>ActivityTaskCompleted</c> event in the workflow history. </para> /// <para><b>IMPORTANT:</b> If the requested task does not complete successfully, use RespondActivityTaskFailed instead. If the worker finds /// that the task is canceled through the canceled flag returned by RecordActivityTaskHeartbeat, it should cancel the task, clean up and then /// call RespondActivityTaskCanceled. </para> <para> A task is considered open from the time that it is scheduled until it is closed. Therefore /// a task is reported as open while a worker is processing it. A task is closed after it has been specified in a call to /// RespondActivityTaskCompleted, RespondActivityTaskCanceled, RespondActivityTaskFailed, or the task has <a href="http://docs.aws.amazon.com/amazonswf/latest/developerguide/swf-dg-basic.html#swf-dev-timeout-types">timed out</a> . /// </para> <para> <b>Access Control</b> </para> <para>You can use IAM policies to control this action's access to Amazon SWF resources as /// follows:</para> /// <ul> /// <li>Use a <c>Resource</c> element with the domain name to limit the action to only specified domains.</li> /// <li>Use an <c>Action</c> element to allow or deny permission to call this action.</li> /// <li>You cannot use an IAM policy to constrain this action's parameters.</li> /// /// </ul> /// <para>If the caller does not have sufficient permissions to invoke the action, or the parameter values fall outside the specified /// constraints, the action fails by throwing <c>OperationNotPermitted</c> . For details and example IAM policies, see <a href="http://docs.aws.amazon.com/amazonswf/latest/developerguide/swf-dev-iam.html">Using IAM to Manage Access to Amazon SWF Workflows</a> /// .</para> /// </summary> /// /// <param name="respondActivityTaskCompletedRequest">Container for the necessary parameters to execute the RespondActivityTaskCompleted service /// method on AmazonSimpleWorkflow.</param> /// /// <exception cref="T:Amazon.SimpleWorkflow.Model.OperationNotPermittedException" /> /// <exception cref="T:Amazon.SimpleWorkflow.Model.UnknownResourceException" /> /// <param name="cancellationToken"> /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// </param> public async Task<RespondActivityTaskCompletedResponse> RespondActivityTaskCompletedAsync(RespondActivityTaskCompletedRequest respondActivityTaskCompletedRequest, CancellationToken cancellationToken = default(CancellationToken)) { var marshaller = new RespondActivityTaskCompletedRequestMarshaller(); var unmarshaller = RespondActivityTaskCompletedResponseUnmarshaller.GetInstance(); var response = await Invoke<IRequest, RespondActivityTaskCompletedRequest, RespondActivityTaskCompletedResponse>(respondActivityTaskCompletedRequest, marshaller, unmarshaller, signer, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); return response; }
internal RespondActivityTaskCompletedResponse RespondActivityTaskCompleted(RespondActivityTaskCompletedRequest request) { var task = RespondActivityTaskCompletedAsync(request); try { return task.Result; } catch(AggregateException e) { throw e.InnerException; } }
/// <summary> /// Respond back to SWF that the activity task is complete /// </summary> /// <param name="taskToken"></param> /// <param name="activityState"></param> void CompleteTask(String taskToken, Common.ActivityTaskCompletedResult activityState) { RespondActivityTaskCompletedRequest request = new RespondActivityTaskCompletedRequest() { Result = Common.Utils.SerializeToJSON<Common.ActivityTaskCompletedResult>(activityState), TaskToken = taskToken }; RespondActivityTaskCompletedResponse response = swfClient.RespondActivityTaskCompleted(request); Console.WriteLine("Activity task completed. RequestActionId " + activityState.RequestActionId); }
static void Worker(string tasklistName) { string prefix = string.Format("Worker{0}:{1:x} ", tasklistName, System.Threading.Thread.CurrentThread.ManagedThreadId); while (true) { Console.WriteLine(prefix + ": Polling for activity task ..."); PollForActivityTaskRequest pollForActivityTaskRequest = new PollForActivityTaskRequest() { Domain = domainName, TaskList = new TaskList() { // Poll only the tasks assigned to me Name = tasklistName } }; PollForActivityTaskResponse pollForActivityTaskResponse = swfClient.PollForActivityTask(pollForActivityTaskRequest); RespondActivityTaskCompletedRequest respondActivityTaskCompletedRequest = new RespondActivityTaskCompletedRequest() { Result = "{\"activityResult1\":\"Result Value1\"}", TaskToken = pollForActivityTaskResponse.ActivityTask.TaskToken }; if (pollForActivityTaskResponse.ActivityTask.ActivityId == null) { Console.WriteLine(prefix + ": NULL"); } else { RespondActivityTaskCompletedResponse respondActivityTaskCompletedResponse = swfClient.RespondActivityTaskCompleted(respondActivityTaskCompletedRequest); Console.WriteLine(prefix + ": Activity task completed. ActivityId - " + pollForActivityTaskResponse.ActivityTask.ActivityId); } } }
/// <summary> /// <para> Used by workers to tell the service that the ActivityTask identified by the <c>taskToken</c> completed successfully with a /// <c>result</c> (if provided). </para> <para> The <c>result</c> appears in the <c>ActivityTaskCompleted</c> event in the workflow history. /// </para> <para><b>IMPORTANT:</b> If the requested task does not complete successfully, use RespondActivityTaskFailed instead. If the worker /// finds that the task is canceled through the canceled flag returned by RecordActivityTaskHeartbeat, it should cancel the task, clean up and /// then call RespondActivityTaskCanceled. </para> /// </summary> /// /// <param name="respondActivityTaskCompletedRequest">Container for the necessary parameters to execute the RespondActivityTaskCompleted service /// method on AmazonSimpleWorkflow.</param> /// /// <exception cref="OperationNotPermittedException"/> /// <exception cref="UnknownResourceException"/> public RespondActivityTaskCompletedResponse RespondActivityTaskCompleted(RespondActivityTaskCompletedRequest respondActivityTaskCompletedRequest) { IRequest<RespondActivityTaskCompletedRequest> request = new RespondActivityTaskCompletedRequestMarshaller().Marshall(respondActivityTaskCompletedRequest); RespondActivityTaskCompletedResponse response = Invoke<RespondActivityTaskCompletedRequest, RespondActivityTaskCompletedResponse> (request, this.signer, RespondActivityTaskCompletedResponseUnmarshaller.GetInstance()); return response; }
public static void Main(string[] args) { // Define the workflows that we know of that event processor will be handling var workflows = new Dictionary<string, Type> { {"CustomerOrderWorkflow", typeof (CustomerOrderWorkflow)}, {"VerifyCustomerWorkflow", typeof (VerifyCustomerWorkflow)} }; // Stopwatch to see how well we are performing var stopwatch = new Stopwatch(); // We will use this ID as our decision task ID and activity task ID to identify ourselves when polling for // decision and activity tasks. var workflowWorkerIdentity = Guid.NewGuid(); // Print out our AWS SWF domains, workflows and activities Console.Write(GetServiceOutput()); var loop = true; do { // Our super simple application menu Console.WriteLine(""); Console.WriteLine("============="); Console.WriteLine("| Main Menu |"); Console.WriteLine("============="); Console.WriteLine("[1] Submit a new workflow"); Console.WriteLine("[2] Wait for decide using a decision task"); Console.WriteLine("[3] Wait for and do some work for an activity task"); Console.WriteLine("[4] Quit"); Console.Write("\nChoice: "); var key = Console.ReadLine(); if (String.IsNullOrEmpty(key)) { continue; } switch (key) { // Initiate a workflow execution case "1": { Console.WriteLine("Option [1] selected - Submit a new workflow"); // SWF client is disposable, so dispose it using (var swfClient = new AmazonSimpleWorkflowClient(RegionEndpoint.USWest2)) { // Our simple property bag: we just need to the email for the account var propertyBag = new Dictionary<string, object> { { "SampleOrderNumber", "12345" } }; // Setup the workflow request var workflowRequest = new StartWorkflowExecutionRequest { Domain = "demo-domain", WorkflowId = Guid.NewGuid().ToString(), WorkflowType = new WorkflowType { Name = "CustomerOrderWorkflow", Version = "1.0" }, Input = JsonConvert.SerializeObject(propertyBag) }; try { // Call AWS SWF and submit the workflow request swfClient.StartWorkflowExecution(workflowRequest); } catch (AmazonSimpleWorkflowException ex) { Console.WriteLine("Caught Exception: " + ex.Message); Console.WriteLine("Response Status Code: " + ex.StatusCode); Console.WriteLine("Error Code: " + ex.ErrorCode); Console.WriteLine("Error Type: " + ex.ErrorType); Console.WriteLine("Request ID: " + ex.RequestId); Console.WriteLine("Data: " + ex.Data); Console.WriteLine("Stacktrace: " + ex.StackTrace); } } } break; // Poll for decision task case "2": { Console.WriteLine("Option [2] selected - Wait for decide using a decision task"); Console.WriteLine("Waiting..."); // SWF client is disposable, so dispose it using (var swfClient = new AmazonSimpleWorkflowClient(RegionEndpoint.USWest2)) { try { // Setup the decision request var decisionTaskRequest = new PollForDecisionTaskRequest { Domain = "demo-domain", Identity = workflowWorkerIdentity.ToString(), TaskList = new TaskList { Name = "DeciderTaskList-Default" } }; // Call AWS SWF and wait for (default timeout: 60 secs) a decision task var decisionTaskResponse = swfClient.PollForDecisionTask(decisionTaskRequest); // Task token being an empty string means there are no tasks available and // we are past the 60 seconds that AWS holds a connection in case a task // becomes available. If this is the case, we simply retry. var taskToken = decisionTaskResponse.DecisionTask.TaskToken; if (!String.IsNullOrEmpty(taskToken)) { // We have a valid task, do something... var decisionTask = decisionTaskResponse.DecisionTask; switch (decisionTask.WorkflowType.Name) { case "CustomerOrderWorkflow": case "VerifyCustomerWorkflow": { Debug.Assert(decisionTask.WorkflowType.Version == "1.0"); } break; default: Console.WriteLine("ERROR: Unknown workflow."); break; } // Define a new WorkflowEventsProcessor object and let it make the decision! stopwatch.Start(); var workflowProcessor = new WorkflowEventsProcessor(decisionTask, workflows, decisionTaskRequest, swfClient); var decisionRequest = workflowProcessor.Decide(); stopwatch.Stop(); Console.WriteLine(">>> Decision(s) made in " + stopwatch.ElapsedMilliseconds + "ms"); // We have our decision, send it away and do something // more productive with the response swfClient.RespondDecisionTaskCompleted(decisionRequest); } } catch (AmazonSimpleWorkflowException ex) { Console.WriteLine("Caught Exception: " + ex.Message); Console.WriteLine("Response Status Code: " + ex.StatusCode); Console.WriteLine("Error Code: " + ex.ErrorCode); Console.WriteLine("Error Type: " + ex.ErrorType); Console.WriteLine("Request ID: " + ex.RequestId); Console.WriteLine("Data: " + ex.Data); Console.WriteLine("Stacktrace: " + ex.StackTrace); } } } break; // Poll for activity task case "3": { Console.WriteLine("Option [3] selected - Wait for decide using a activity task"); Console.WriteLine("Waiting..."); // SWF client is disposable, so dispose it using (var swfClient = new AmazonSimpleWorkflowClient(RegionEndpoint.USWest2)) { try { // Setup the activity request var activityTaskRequest = new PollForActivityTaskRequest { Domain = "demo-domain", Identity = workflowWorkerIdentity.ToString(), TaskList = new TaskList { Name = "ActivityTaskList-Default" } }; // Call AWS SWF and wait for (default timeout: 60 secs) a activity task var activityTaskResponse = swfClient.PollForActivityTask(activityTaskRequest); // Task token being an empty string means there are no tasks available and // we are past the 60 seconds that AWS holds a connection in case a task // becomes available. If this is the case, we simply retry. var taskToken = activityTaskResponse.ActivityTask.TaskToken; if (!String.IsNullOrEmpty(taskToken)) { // We have a valid task, do something... var activityTask = activityTaskResponse.ActivityTask; Console.WriteLine("\n"); Console.WriteLine(">>> Activity: " + activityTask.ActivityType.Name); // In the real world we would define the activity code in a separate object // and fire off a thread to actually work on it but in this case we are just // testing the workflow so this suffices switch (activityTask.ActivityType.Name) { // CustomerOrderWorkflow activities case "VerifyOrder": case "ShipOrder": { Debug.Assert(activityTask.ActivityType.Version == "1.0"); } break; // VerifyCustomerWorkflow activities case "VerifyCustomerAddress": case "CheckFraudDB": case "ChargeCreditCard": { Debug.Assert(activityTask.ActivityType.Version == "1.0"); } break; default: Console.WriteLine("ERROR: Unknown activity."); break; } var activityCompletedRequest = new RespondActivityTaskCompletedRequest { TaskToken = activityTask.TaskToken, Result = activityTask.Input }; // Completion request setup complete, send it away. NOTE: Do something more // productive with the response swfClient.RespondActivityTaskCompleted(activityCompletedRequest); //var activityFailedRequest = new RespondActivityTaskFailedRequest // { // TaskToken = activityTask.TaskToken, // Details = "Test failure." // }; //// Completion request setup complete, send it away. NOTE: Do something more //// productive with the response //swfClient.RespondActivityTaskFailed(activityFailedRequest); } } catch (AmazonSimpleWorkflowException ex) { Console.WriteLine("Caught Exception: " + ex.Message); Console.WriteLine("Response Status Code: " + ex.StatusCode); Console.WriteLine("Error Code: " + ex.ErrorCode); Console.WriteLine("Error Type: " + ex.ErrorType); Console.WriteLine("Request ID: " + ex.RequestId); Console.WriteLine("Data: " + ex.Data); Console.WriteLine("Stacktrace: " + ex.StackTrace); } } } break; case "4": // Quit loop = false; break; default: Console.WriteLine("ERROR: Unknown command."); break; } } while (loop); }