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.
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.
Related
In my WCF service, I have to make a call to an API, where I wanted to do a Fire and Forget implementation. And If possible just capture the errors if any.(That's fine too , if not an option)
I am planning to do the following implementation, what are the issues it could lead to? By doing the following implementation is going to leave a huge number of open connections. Or what could be the issue? Please help in understanding how in a better way this can be implemented.
void SendRequest(inputs)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
request.Method = "POST";
request.ContentType = "application/xml";
byte[] requestBytes = Encoding.UTF8.GetBytes(inputXML);
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(requestBytes, 0, requestBytes.Length);
}
request.GetResponseAsync();
}
Main()
{
try
SendRequest(inputs);
catch ex
log ex;
}
First, make fully async version of your code
using System.Threading;
public async Task<System.Net.WebResponse> SendRequestAsync(
string inputXML, string url, CancellationToken cancellationToken)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/xml";
byte[] requestBytes = Encoding.UTF8.GetBytes(inputXML);
// GetRequestStreamAsync is a lightweight operation I assume
// so we don't really need any cancellation
using (Stream requestStream = await request.GetRequestStreamAsync())
{
// Idk if we really need cancellation here, but just in case of big request we might need to
await requestStream.WriteAsync(
requestBytes, 0, requestBytes.Length, cancellationToken);
}
// Close any long-running request
using (cancellationToken.Register(() => request.Abort(), useSynchronizationContext: false))
{
var response = await request.GetResponseAsync();
cancellationToken.ThrowIfCancellationRequested();
return response;
}
}
Let's create an async void method, but make it safe. It will basically execute in "fire-and-forget" manner.
public async void DoWork(string inputXML, string url, CancellationToken ct)
{
try
{
using(var response = await SendRequestAsync(inputXML, url, ct))
{
var httpResponse = (HttpWebResponse) response;
// Use 201 Created or whatever you need
if (httpResponse.StatusCode != HttpStatusCode.Created)
{
// TODO: handle wrong status code
}
}
}
catch (Exception e)
{
if (ct.IsCancellationRequested)
{
Console.WriteLine("Cancelled");
}
else
{
// TODO: handle exception
}
}
}
private static CancellationTokenSource _cts = new CancellationTokenSource();
public static void Main (string[] args)
{
DoWork("xml string", "example.com", cts.Token);
Console.WriteLine("Boom!");
if (Console.ReadLine() == "exit")
{
// Cancel the damn job
cts.Cancel();
}
}
It's important to handle all errors from inside a DoWork, because following will not work
// Warning, will NOT catch an exception
public static void Main (string[] args)
{
try
{
DoWork("xml string", "example.com");
}
catch (Exception e)
{
}
}
EDIT: OP requested cancellation so I added cancellation
Please note that it's not best practice not to use fire and forget, especially if this a core layer of the application and you might miss important exceptions. When you use this technique you have to remember that the following happens:
Exception will be fail silently without any chance of catching them. normally you will want to log them or get a notification.
You have no idea when the code completes,
Since You don't need the code to complete and it might may not run to you would have no notification that it failed to complete.
A good case scenario for using this technique could be for updating a cache for an example.
Having said that, you could use the following techniques:
NET 4.5 allows us to use it via Task.Run
Task.Run(() => FireAndForget());
You could also start a thread with parameterless lambda:
(new Thread(() => {
FireAndForget();
}) {
Name = "Running Work Thread (FireAndForget)",
Priority = ThreadPriority.BelowNormal
}).Start();
I've been reading Essential C# 6.0 recently. In the chapter of the book where author explains multi threading he shows this method and I don't understand two things about it which don't seem to be explained anywhere.
private static Task WriteWebRequestSizeAsync(string url)
{
StreamReader reader = null;
WebRequest webRequest = WebRequest.Create(url);
Task task = webRequest.GetResponseAsync()
.ContinueWith(antecedent =>
{
WebResponse response = antecedent.Result;
reader = new StreamReader(response.GetResponseStream());
return reader.ReadToEndAsync();
})
.Unwrap()
.ContinueWith(antecedent =>
{
if(reader != null) reader.Dispose();
string text = antecedent.Result;
Console.WriteLine(text.Length);
});
return task;
}
1. Why does the author use ContinueWith() methods and calls them essential? How is his way of doing it better than my approach, which does not utilize these methods?
private static Task WriteWebRequestSizeAsync(string url)
{
return Task.Run(() =>
{
WebRequest webRequest = WebRequest.Create(url);
WebResponse response = webRequest.GetResponseAsync().Result;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string text = reader.ReadToEndAsync().Result;
Console.WriteLine(text.Length);
}
});
}
2. Why does the author use async variants of the methods and then access their result via .Result property, instead of using not async variants as it appears to have the same result at the end. Please, notice that I haven't changed it in my approach above
Although you are calling GetResponseAsync() in your method, however, trying to use .Result makes it a blocking call.As a result of this your task continues to wait for the result to be available wasting cpu cycles.
WebResponse response = webRequest.GetResponseAsync().Result; //blocking call
However, in the example by author, GetResponseAsync() is followed by a ContinueWith(). This means that Task on which GetResponseAsync() is called won't be blocked and can be utilized to do something else. When the result of GetResponseAsync() is available the continuation will run.
webRequest.GetResponseAsync()
.ContinueWith(antecedent =>
{
WebResponse response = antecedent.Result;
reader = new StreamReader(response.GetResponseStream());
return reader.ReadToEndAsync();
})
Same example can also be written using async and await instead of continuation...This will have a similar effect of continuations . However, this will be more natural to read.
var result = await webRequest.GetResponseAsync();
//do something with result now.
It seems as if the author uses stacked continuation in order to split the operations according to the seperation of concerns principle.
Main difference between yours and authors way is that author runs code in the same thread from what method WriteWebRequestSizeAsync while your code will run in some thread from ThreadPull.
I don't know context so may be it's essential.
About second question. If author calls not async methods he could not get tasks and attach to them ContinueWith.
On .NET Core, using a System.Net.Http.HttpClient, is it safe to first call response.Content.ReadAsStringAsync(), and then call response.Content.ReadAsStreamAsync()?
For example, do something like this.
var response = await client.GetAsync("http://example.com");
var respStr = await response.Content.ReadAsStringAsync();
// ... Do something with the string
var respStream = await response.Content.ReadAsStreamAsync();
// ... Do something with the stream
I'm worried about the response content being streamed so that it's not possible to read it twice.
I have tested it with a couple of requests, and it always works for me, but is it guaranteed to work?
Yes, it is safe as long as content is not disposed.
If you look how those methods are implemented in HttpContent class (response.Content returns the instance), you will see that memory buffer is used internally.
public Task<string> ReadAsStringAsync()
{
CheckDisposed();
return WaitAndReturnAsync(LoadIntoBufferAsync(), this, s => s.ReadBufferedContentAsString());
}
and
public Task<Stream> ReadAsStreamAsync()
{
CheckDisposed();
ArraySegment<byte> buffer;
if (_contentReadStream == null && TryGetBuffer(out buffer))
{
_contentReadStream = Task.FromResult<Stream>(new MemoryStream(buffer.Array, buffer.Offset, buffer.Count, writable: false));
}
if (_contentReadStream != null)
{
return _contentReadStream;
}
_contentReadStream = CreateContentReadStreamAsync();
return _contentReadStream;
}
List<string> urls = this.populateRequestList();
this.Logger("Starting");
var reqs = urls.Select<string, WebRequest>(HttpWebRequest.Create).ToArray();
var iars = reqs.Select(req => req.BeginGetResponse(null, null)).ToArray();
var rsps = reqs.Select((req, i) => req.EndGetResponse(iars[i])).ToArray();
this.Logger("Done");
Things I noticed so far:
When I run this code, "Starting" shows up in my log, but "Done" never shows up. When I view the whole process in the debugger, it seems to skip over it like it's not even there. No exceptions are being thrown either. When reqs.Select is looping through req.EndGetResponse(iars[i]), it's like it freezes or skips over stuff. When I view it in the debugger, I don't get past 10-15 loops before it just skips to the end.
Questions:
How do I stop this from "skipping" sometime during var rsps = reqs.Select((req, i) => req.EndGetResponse(iars[i])).ToArray();?
How to I get the html from rsps? I think this problem doing that stems from the "skipping". I tried looping through each response and calling Repsponse.GetResponseStream() etc..., but nothing happens as soon as it skips.
The problem with your code is that BeginGetResponse(null, null) accepts a callback as the first argument which is invoked when the operation completes. This callback is where EndGetResponse should be called. When you call EndGetResponse, the operations are not yet completed.
Look at this article to see how aync web requests can be made in C# using iterators: http://tomasp.net/blog/csharp-async.aspx.
If using the task parallel library or .NET 4 you can also do this:
var urls = new List<string>();
var tasks = urls.Select(url =>
{
var request = WebRequest.Create(url);
var task = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
task.Start();
return task;
}).ToArray();
Task.WaitAll(tasks);
foreach (var task in tasks)
{
using (var response = task.Result)
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
var html = reader.ReadToEnd();
}
}
You are trying to use the asynchronous request methods to do a synchronous request, that doesn't work.
You are supposed to start the requests using BeginGetResponse with a callback method that handles each response. If you call EndGetResponse immediately after BeginGetResponse, it will fail because the response haven't started to arrive yet.
If you want to make a synchronous request, use the GetResponse method instead.
As I read http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.endgetresponse.aspx you need to wait for the callback before you can use EndGetResponse?
Or use GetReponse: http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.getresponse.aspx
I'm using HttpWebRequest to call a web service. If the AsyncCallback from BeginGetResponse throws an error, I want to propagate it up to my main program flow. I'm having trouble doing this because the error doesn't get propagated beyond the AsyncCallback. I've tried putting try/catch blocks at each step of the HttpWebRequest chain, but it never propagates beyond the "ResponseCallBack" method. Is it possible to get it back to the main thread?
private void StartRequest()
{
// Code to create request object is here
// ...
httpRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), httpRequest);
}
private void GetRequestStreamCallback(IAsyncResult result)
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
// End the operation
var postStream = request.EndGetRequestStream(result);
string body = GenerateRequestBody();
// Convert the string into a byte array
byte[] postBytes = Encoding.UTF8.GetBytes(body);
// Write to request stream
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();
// Start the asynchronous operation to get the resonse
try
{
request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
}
catch (Exception)
{
throw;
}
}
private void ResponseCallback(IAsyncResult result)
{
string contents = String.Empty;
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
contents = reader.ReadToEnd();
}
// Check the status
if (response.StatusCode == HttpStatusCode.OK)
{
//EXCEPTION NEVER MAKES IT PASSED HERE, EVEN IF I HAVE IT IN A TRY/CATCH BLOCK AND RE-THROW IT.
_object = ProcessResponseEntity(contents);
}
}
I think you're getting confused about the way asynchronous code exectution works and how the callback execution fits in with the calling code.
Within GetRequestStreamCallback, after the call to request.BeginGetResponse the method will continue to execute and in your example just end.
It is not known when (or even if) ResponseCallback will execute or what will be happening on the UI thread when it does. Because of this, ResponseCallback will execute on a different thread.
It's possible to have code within the callback run on the UI thread (which you'll need to do you want to interact with the UI) by using Dispatcher.BeginInvoke. However you can't have this execute within the context of another method.
While I wouldn't recommend it, you may want to have a look at this discussion on making the callback appear to execute synchronously. This will block your UI thread though and so is NOT recommended.