In my previous blog post Invoke Workflow (WF) using Task-based Asynchronous Pattern (TAP) in ASP.NET MVC, I mentioned that I've created a CodeActivity namely GetDeal. This is actually an AsyncCodeActivity which make the http request using the new HttpClient. This is a little tricky because AsyncCodeActivity is based on the APM pattern (Asynchronous Programming Model pattern aka IAsyncResult pattern aka Begin/End pattern) and the new HttpClient calls are based on TAP pattern (Task-based Asynchronous Pattern). In order to resolve this, I've essentially followed the approach mentioned on msdn here and created an extension method for Task<TResult>.
Within the BeginExecute, I call this method on the Task object returned by HttpClient.GetAsync(string) and then within the EndExecute, I read the HttpResponseMessage from the Task object.
[Note: Since we need the HttpClient instance till the task duration, I've defined the disposing of it within the Task.ContinueWith]
namespace Activities.SmartPea
{
public sealed class GetDeal : AsyncCodeActivity
{
public InArgument<string> ItemName { get; set; }
public InArgument<string> Zip { get; set; }
public OutArgument<IEnumerable<Deal>> Deals { get; set; }
protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback,
object state)
{
var httpClient = new HttpClient();
var task =
httpClient.GetAsync(string.Format("http://api.smartpea.com/api/deal/?title={0}&zip={1}",
ItemName.Get(context), Zip.Get(context)))
.ToApm(callback,
state);
task.ContinueWith(obj =>
httpClient.Dispose());
return task;
}
protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
{
HttpResponseMessage response = ((Task<HttpResponseMessage>)result).Result;
IEnumerable<Deal> deals = new List<Deal>();
if (response.IsSuccessStatusCode &&
string.Compare(response.Content.Headers.ContentType.MediaType, "application/json",
StringComparison.OrdinalIgnoreCase)
== 0)
deals =
response.Content.ReadAsAsync<IEnumerable<Deal>>().Result; //.ToObject<IEnumerable<Deal>>();
Deals.Set(context,deals);
}
}
public static class Extensions
{
public static Task<TResult> ToApm<TResult>(this Task<TResult> task, AsyncCallback
callback, object state)
{
if (task.AsyncState == state)
{
if (callback != null)
{
task.ContinueWith(delegate { callback(task);
},
CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
}
return task;
}
var tcs = new TaskCompletionSource<TResult>(state);
task.ContinueWith(obj =>
{
if (task.IsFaulted)
tcs.TrySetException(task.Exception.InnerExceptions);
else if (task.IsCanceled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(task.Result);
if (callback != null) callback(tcs.Task);
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
return tcs.Task;
}
}
}
Cheers!
Twisha
No comments:
Post a Comment