WebClient.OpenReadCompleted - how to wait for this event to finish - c#

So basiacally, I'm creating a dll project which have to get some info from the bing maps api. Below I'm pasting a method which is resposible for getting and serializing the response:
private void GetResponse(Uri uri, Action<Response> callback)
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += (o, a) =>
{
if (callback != null)
{
DataContractJsonSerializer responseSerializer = new DataContractJsonSerializer(typeof(Response));
callback(responseSerializer.ReadObject(a.Result) as Response);
}
};
wc.OpenReadAsync(uri);
}
Below method is using GetResponse method to get lattitudes:
public Lattitudes getLattitudes()
{
Lattitudes lattitudes = new Lattitudes();
GetResponse(geocodeRequestURI, (x) =>
{
lattitudes.SouthLatitude = x.ResourceSets[0].Resources[0].BoundingBox[0];
lattitudes.SouthLatitude = x.ResourceSets[0].Resources[0].BoundingBox[1];
lattitudes.SouthLatitude = x.ResourceSets[0].Resources[0].BoundingBox[2];
lattitudes.SouthLatitude = x.ResourceSets[0].Resources[0].BoundingBox[3];
});
return lattitudes;
}
My problem is that second method is returning empty object and part inside GetResponse method is being executed later. Is There a way to wait for this event to finish and then return, or maybe I need to redesign it ?(max version of .NET I can use is 4.0)

I suggest if you want to wait than make use of Task by converting method competable with async and await like as below
private async Task RequestDataAsync(string uri, Action<string> action)
{
var client = new WebClient();
string data = await client.OpenReadAsync(uri);
action(data);
}
call method from the code
Task t = RequestDataAsync(parameter)
t.Wait();
this help more : http://www.dotnetperls.com/async

Related

Best way to call and manage response for many http request

I have a challenge, I need to call many http request and handle each of them.
How to do it, I don't want to wait for get response from one of them and then call next, how to assign a method for process response (like callback).
How can define callback and assign to each of them ?
What you need is an Asynchronous programming model where you create async tasks and later use await keyword for the response.
So essentially you are not waiting for the first async call to finish, you'd just fire as many async tasks as you wish and wait to get a response only when you need the response to move ahead with your program logic.
Have a look at below for more details:
https://msdn.microsoft.com/en-us/library/hh696703.aspx
1) you can call that normaly(noneasync):
public string TestNoneAsync()
{
var webClient = new WebClient();
return webClient.DownloadString("http://www.google.com");
}
2) you can use APM (async):
private void SpecAPI()
{
var req = (HttpWebRequest)WebRequest.Create("http://www.google.com");
//req.Method = "HEAD";
req.BeginGetResponse(
asyncResult =>
{
var resp = (HttpWebResponse)req.EndGetResponse(asyncResult);
var headersText = formatHeaders(resp.Headers);
Console.WriteLine(headersText);
}, null);
}
private string formatHeaders(WebHeaderCollection headers)
{
var headerString = headers.Keys.Cast<string>()
.Select(header => string.Format("{0}:{1}", header, headers[header]));
return string.Join(Environment.NewLine, headerString.ToArray());
}
3) you can create a callback and asign it,EAP.(async .net 2):
public void EAPAsync()
{
var webClient = new WebClient();
webClient.DownloadStringAsync(new Uri("http://www.google.com"));
webClient.DownloadStringCompleted += webClientDownloadStringCompleted;
}
void webClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
// use e.Result
Console.WriteLine("download completed callback.");
}
4) you can use newer way TAP, cleare way (c# 5). it's recommended:
public async Task<string> DownloadAsync(string url)
{
var webClient = new WebClient();
return await webClient.DownloadStringTaskAsync(url);
}
public void DownloadAsyncHandler()
{
//DownloadAsync("http://www.google.com");
}
threading in this solution is't good approch.(many threads that pending to call http request!)

How to make the return call wait for async complete c#

I am calling into this static login method with username and password. I want it to wait to return until the downloadstringasync has completed. For the freaking life of me I cant get it to wait though.
I tried the oldschool
while(wc.IsBusy){} //Which froze
also tried a variety of async crap that didnt even compile
public static dbObj Login(String username, String password)
{
dbObj ret = new dbObj();
String rawWebReturn = "";
ret.propBag.Add(_BAGTYPE,returnTypes.Login.ToString());
DateTime date = DateTime.Now;
WebClient wc = new WebClient();
wc.DownloadStringAsync(new Uri(baseLoginURI + "uname=" + username + "&pword=" + password + "&date=" + date.ToString()));
wc.DownloadStringCompleted += (s,e) => {
if(e.Error!=null)
rawWebReturn = e.Error.Message;
else
rawWebReturn = e.Result;
};
return parseWebReturn(rawWebReturn,ret);
}
First of all, you should never ever wait for an asynchronous operation to finish. Instead, continue with your execution after the function completes.
EDIT: You must add NuGet package Microsoft.Bcl.Async here.
Now, since you are using Windows Phone 8, you can safely use async here:
public static async dbObj Login(String username, String password)
{
dbObj ret = new dbObj();
String rawWebReturn = "";
ret.propBag.Add(_BAGTYPE, returnTypes.Login.ToString());
DateTime date = DateTime.Now;
WebClient wc = new WebClient();
try
{
var result = await wc.DownloadStringTaskAsync(new Uri(baseLoginURI + "uname=" + username + "&pword=" + password + "&date=" + date.ToString()));
return parseWebReturn(result, ret);
}
catch (Exception e)
{
return parseWebReturn(e.Message, ret);
}
}
Async must compile because it just works. If you are having trouble with it, ask again with your code snippet here.
If you want to target Windows Phone 7, add the above mentioned NuGet package and you will be able to compile it without problems.
If you need to block the thread until an async operation is completed, you can use ManualResetEvent, like this:
ManualResetEvent wait = new ManualResetEvent(false);
wc.DownloadStringCompleted += (s,e) => {
// ...
// allow to proceed
wait.Set();
};
// wait until Set
wait.WaitOne();
return parseWebReturn(rawWebReturn,ret);
In general though, you don't want to block threads, but rather use callbacks. You can do that by providing an Action delegate instead of the return value:
public static void Login(String username, String password, Action<dbObj> callback)
{
// ...
wc.DownloadStringCompleted += (s,e) => {
if(e.Error!=null)
rawWebReturn = e.Error.Message;
else
rawWebReturn = e.Result;
Callback(parseWebReturn(rawWebReturn, ret););
};
}
If you force a wait in the UI thread either by spinning a while loop or using ManualResetEvent.WaitOne then you are actually deadlocking your app. The network call at some point appears to need to touch the UI thread, but you've frozen it = deadlock.
Freezing the UI thread like this is also bad in lots of other ways (no touch events will be processed, no screen updates etc).
You would be better off using HttpClient for Windows Phone, and using the async/await pattern. You can download this from NuGet as the Microsoft.Net.Http package.
You can rewrite the existing WebClient method with the help of Async and Await method something like this
public Task<string> GetRssFeed(string feedUrl)
{
var tcs = new TaskCompletionSource<string>();
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
if (e.Error == null)
{
tcs.SetResult(e.Result);
}
else
{
tcs.SetException(e.Error);
}
};
client.DownloadStringAsync(new Uri(feedUrl));
return tcs.Task;
}
So from doing so you can await your call until the callback come back. This is the most effective and latest way to achieve your functionality

Need to make synchronous REST calls in WP8 using HttpWebRequest

I've looked at some of the answers for similar questions and can't seem to find something that is applicable to what I'm doing. I need to make a few synchronous requests using HttpWebRequest (some using each verb, GET/PUT/POST/DELETE) and can't seem to get it to work. The example below works great when I manually use a 'refresh' button that I have in the design (works for any verb specified), but when I uncomment the section in 'b_send_Click' it doesn't work. What I'm looking for is a wrapper method that will encapsulate the REST client (the way 'b_send_Click' does in this example) and then take some action when the call is complete (the commented section in 'b_send_Click' as an example). Any ideas? By the way, this works well as a wrapper for async REST calls but I can't get the sync working...
using Microsoft.Phone.Controls;
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Windows;
namespace WP8Rest
{
public partial class MainPage : PhoneApplicationPage
{
// global variables
public static string url = "http://mywebsite.com/API/some_api";
public static string request_body = "";
public static HttpWebRequest client = null;
public static HttpWebResponse response = null;
public static string server_response = "";
public static bool request_done = false;
public MainPage()
{
InitializeComponent();
}
private void b_send_Click(object sender, RoutedEventArgs e)
{
rest_request(sender, e);
/*
while (!request_done)
{
Thread.Sleep(100);
}
if (response != null)
{
l_status_code.Text = response.StatusCode.ToString();
l_status_description.Text = response.StatusDescription.ToString();
l_response.Text = server_response;
}
else
{
l_status_code.Text = "0";
l_status_description.Text = "Unable to complete request...";
l_response.Text = "Unable to complete request...";
}
*/
}
private void rest_request(object sender, RoutedEventArgs e)
{
request_done = false;
server_response = "";
request_body = tb_reqbody.Text;
client = (HttpWebRequest)WebRequest.Create(url);
client.Method = tb_verb.Text;
client.AllowAutoRedirect = true;
switch (tb_verb.Text)
{
case "GET":
client.BeginGetResponse(new AsyncCallback(GetResponseCallback), client);
break;
case "PUT":
client.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), client);
client.ContentType = "application/json";
break;
case "POST":
client.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), client);
client.ContentType = "application/json";
break;
case "DELETE":
client.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), client);
client.ContentType = "application/json";
break;
default:
MessageBox.Show("Use GET, PUT, POST, or DELETE.");
return;
}
l_response.Text = "Request sent...";
return;
}
private static void GetRequestStreamCallback(IAsyncResult async_result)
{
HttpWebRequest request = (HttpWebRequest)async_result.AsyncState;
Stream request_body_stream = request.EndGetRequestStream(async_result);
byte[] request_body_bytearray = Encoding.UTF8.GetBytes(request_body);
request_body_stream.Write(request_body_bytearray, 0, request_body.Length);
request_body_stream.Close();
request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
private static void GetResponseCallback(IAsyncResult async_result)
{
HttpWebRequest request = (HttpWebRequest)async_result.AsyncState;
response = (HttpWebResponse)client.EndGetResponse(async_result);
Stream response_body_stream = response.GetResponseStream();
StreamReader stream_reader = new StreamReader(response_body_stream);
server_response = stream_reader.ReadToEnd();
response_body_stream .Close();
stream_reader.Close();
response.Close();
request_done = true;
}
private void b_refresh_Click(object sender, RoutedEventArgs e)
{
if (response != null)
{
l_response.Text = server_response;
l_status_code.Text = response.StatusCode.ToString();
l_status_description.Text = response.StatusDescription.ToString();
}
else
{
l_response.Text = "No response...";
}
}
}
}
Per #Industry86 I was able to get Microsoft.Net.Http installed. Changed code to:
private async void b_send_Click(object sender, RoutedEventArgs e)
{
l_response.Text = myMethod();
}
async Task<string> myMethod()
{
string address = "http://dev.getcube.com:65533/rest.svc/API/mirror";
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(address);
string responseText = await response.Content.ReadAsStringAsync();
return responseText;
}
Problem now is that it won't compile - "Cannot implicitly convert type 'System.Threading.Tasks.Task' to 'string'. I changed the line in b_send_Click to l_response.Text = (myMethod()).Result; (not sure if this is correct or not) and when I click the 'b_send' button, it turns orange and the server never sees the request.
Creating a new answer to focus on your code changes.
put an await in front of your async method call:
private async void b_send_Click(object sender, RoutedEventArgs e)
{
l_response.Text = await myMethod();
}
when you use .Result, it halts the process until the result returns, thereby halting UI and everything else. Here is an SO answer detailing out the problems with this:
https://stackoverflow.com/a/13703845/311393
Quick lesson: with a void return value, an async method will "fire-and-forget" and never expect a return. With a Task or Task<T> return value, an async method called will halt the calling method until it's completed.
Thus b_send_Click will fire off a thread to do whatever. When it calls myMethod(), which is a Task, with the appropriate await keyword, it will stop in a synchronous fashion and wait till it's completed whatever it's doing.
And myMethod() has multiple async method calls but those have await's on them as well, so the thread will wait for those to complete synchronously as well.
Then it returns back to your text field and the UI, I assume in WP8, listens to changes to it's text field asynchronously.
Personally, I would use the HTTPClient that exists in Windows 8 (it's pretty awesome) and currently possibly still in beta for WP8 (https://nuget.org/packages/Microsoft.Net.Http) if not fully released already. The syntax is much smaller and simpler.
Or use RestSharp (http://restsharp.org/) but the async/await stuff isn't as robust. RestSharp is really good at serialization though.
that said, you also need to learn how Async operations occur. You are calling an Asynchronous operation and moving on without an "await". Therefore:
l_response.Text = server_response;
will not get set because, without the Thread.Sleep(100), the code will have fired off the async call and move on and server_response will still be null by the time it gets to that part.
if you want to want to wait for the return of that call, you need to use an "await" command and include the async signifier in the method declaration and return a Task (where object can be whatever you're intending to return). Example using HttpClient:
async Task<string> myMethod()
{
string address = "http://mywebsite.com/API/some_api";
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(address);
string responseText = await response.Content.ReadAsStringAsync();
return responseText;
}
and the resulting string "responseText" will have the JSON content to parse. Of course, if you're looking for a stream, its there too.
And you also have to remember that this method itself will require an await from the UI thread and the declaration will be required to be async:
private async void b_send_Click(object sender, RoutedEventArgs e)
{
someTextBox.Text = myMethod();
}
and event handlers should typically be the only async methods that return void.

C# Threading with functions that return variables

Okay so basically I have a function that returns a string, but to get that string it uses webrequest which means while it's doing that webrequest the form is locking up unless I put it in a different thread.
But I can't figure out a way to capture the returned data in a thread since it's started using thread.start and that's a void.
Any help please?
Current code if it matters to anyone:
string CreateReqThread(string UrlReq)
{
System.Threading.Thread NewThread = new System.Threading.Thread(() => CreateReq(UrlReq));
string ReturnedData = "";
return ReturnedData;
}
string CreateReq(string url)
{
try
{
WebRequest SendReq = WebRequest.Create(url);
SendReq.Credentials = CredentialCache.DefaultCredentials;
SendReq.Proxy = WebRequest.DefaultWebProxy; //For closed port networks like colleges
SendReq.Proxy.Credentials = CredentialCache.DefaultCredentials;
SendReq.Timeout = 15000;
System.IO.StreamReader Reader = new System.IO.StreamReader(SendReq.GetResponse().GetResponseStream());
string Response = Reader.ReadToEnd();
Reader.Close();
return Response;
}
catch (WebException e)
{
EBox(e.Message, "Unknown Error While Connecting");
return null;
}
}
A common means of doing this is to use a Task<T> instead of a thread:
Task<string> CreateReqThread(string UrlReq)
{
return Task.Factory.StartNew() => CreateReq(UrlReq));
// In .NET 4.5, you can use (or better yet, reimplement using await/async directly)
// return Task.Run(() => CreateReq(UrlReq));
}
You can then call Task<T>.Result to get the returned value (later), when it's needed, or schedule a continuation on the task which will run when it completes.
This could look something like:
var request = CreateReqThread(theUri);
request.ContinueWith(t =>
{
// Shove results in a text box
this.textBox.Text = t.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
This also works perfectly with the new await/async support in C# 5.

Convert Event based pattern to async CTP pattern

_fbClient.GetCompleted += new EventHandler<FacebookApiEventArgs>(OnFetchPageNotification);
_fbClient.GetAsync(_kNotificationPath, new Dictionary<string, object> { { "access_token", _kPageAccessToken } });
How to convert above code into awaitable code in wp7:
object = await _fbClient.GetAsync(_kNotificationPath, new Dictionary<string, object> { { "access_token", _kPageAccessToken } });
I have CTP Installed and task parallel library also.
The Async CTP came with a document that describes how to adapt each existing pattern to the Task Based Async pattern. It says that the Event based one is more variable, but does give one example:
public static Task<string> DownloadStringAsync(Uri url)
{
var tcs = new TaskCompletionSource<string>();
var wc = new WebClient();
wc.DownloadStringCompleted += (s,e) =>
{
if (e.Error != null) tcs.TrySetException(e.Error);
else if (e.Cancelled) tcs.TrySetCanceled();
else tcs.TrySetResult(e.Result);
};
wc.DownloadStringAsync(url);
return tcs.Task;
}
Where the original function that's being wrapped is DownloadStringAsync, the parameters match the parameters being passed to this function, and DownloadStringCompleted is the event that is being monitored.
(The same document appears to be downloadable here - the above sample (and more description) are from "Tasks and the Event-based Asynchronous Pattern (EAP)")

Categories