Reputation: 9232
I have the following extension methods for using asynchronous calls on WebRequest objects.
public static Task<WebResponse> GetReponseAsync(this WebRequest request)
{
return Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
}
public static Task<Stream> GetRequestStreamAsync(this WebRequest request)
{
return Task.Factory.FromAsync<Stream>(request.BeginGetRequestStream, request.EndGetRequestStream, null);
}
I would like to convert the following code to an asynchronous equivalent using these extension methods.
using (Stream rs = request.GetRequestStream())
{
var postData = Encoding.ASCII.GetBytes(PostData);
rs.Write(postData, 0, postData.Length);
using (WebResponse response = request.GetResponse())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
str = reader.ReadToEnd();
rs.Close();
reader.Close();
response.Close();
}
}
I did this easily using another piece of code making usage of WebRequest, however that did not require the first call to GetRequestStream().
request.GetReponseAsync().ContinueWith(t =>
{
if (t.Exception == null)
{
using (var sr = new StreamReader(t.Result.GetResponseStream()))
{
str = sr.ReadToEnd();
}
}
});
How do I convert the first code block to use my extension methods and be equivalent?
EDIT I am using .NET 4.0, so async/await is not currently an option.
Upvotes: 2
Views: 3067
Reputation: 6155
You just need to chain the ContinueWith
calls. As a rule of thumb, you will have one ContinueWith per async operation in the sequence. Each ContinueWith will generally end with return <some async call>
and the next will start process its result.
request.GetRequestStreamAsync()
.ContinueWith((trs) =>
{
var postData = System.Text.Encoding.ASCII.GetBytes("dummy");
trs.Result.Write(postData, 0, postData.Length);
return request.GetResponseAsync();
}).Unwrap()
.ContinueWith((resp) =>
{
using (var sr = new StreamReader(resp.Result.GetResponseStream()))
{
var str = sr.ReadToEnd();
}
});
Note that in my code (and your asynchronous version), not all of the objects are getting disposed as they were in the original.
At each step, you probably want to either check the Status or IsFaulted/IsCanceled properties or use the overload of ContinueWith that takes a TaskContinuationOptions parameter. For the latter option, beware that the previous task not completing in a way that matches the options results in a cancelled task. If you need to pass the errors through, that approach will not. Personally, I wrap all the checking into a method that either passes through errors and cancellation or runs a delegate on successful completion. Otherwise, you get a lot of boilerplate checking code very quickly.
Upvotes: 3