Im trying to make an async method that returns a value. everything work when use the method without return. you can process data , but the problem appears when the return clause added. the program freeze completely without any error or for a while.
please see the code:
public void runTheAsync(){
string resp = sendRequest("http://google.com","x=y").Result;
}
public async Task<string> sendRequest(string url, string postdata)
{
//There is no problem if you use void as the return value , the problem appears when Task<string> used. the program fully go to freeze.
Console.WriteLine("On the UI thread.");
string result = await TaskEx.Run(() =>
{
Console.WriteLine("Starting CPU-intensive work on background thread...");
string work = webRequest(url,postdata);
return work;
});
return result;
}
public string webRequest(string url, string postdata)
{
string _return = "";
WebClient client = new WebClient();
byte[] data = Encoding.UTF8.GetBytes(postdata);
Uri uri = new Uri(url);
_return = System.Text.Encoding.UTF8.GetString(client.UploadData(uri, "POST", data));
return _return;
}
string resp = sendRequest("http://google.com","x=y").Result;
That's your problem. If you call Result on a Task, it blocks until the Task finishes.
Instead, you can do this:
public async void runTheAsync()
{
string resp = await sendRequest("http://google.com","x=y");
}
But creating async void methods should be avoided. Whether you actually can avoid it, depends on how are you calling it.
Try this, data correctness checks etc. omitted but you ignored them either :-):
public async Task<string> UploadRequestAsync(string url, string postdata)
{
string result = await Encoding.GetString(
new WebClient().UploadData(new Uri(uri), "POST", Encoding.UTF8.GetBytes(postdata)));
return result;
}
You somehow doing the work twice, awaiting a explicitly started task. I'd be curious to see what the generated code for this looks like... And of course, in production code use the proper classes from .NET 4.5.
Related
The following Code works within a console application.
public Boolean Graph_IsMemberOfGroup(string Parm_AzureUserID, string Parm_GroupID) {
Boolean Lcl_ReturnValue = false;
Task<string> Lcl_Response = GraphPriv_IsMemberOfGroup(Parm_AzureUserID, Parm_GroupID);
if (Lcl_Response.Result != null) {
Lcl_ReturnValue = Lcl_Response.Result.Contains(Parm_GroupID);
}//end if
return (Lcl_ReturnValue);
}
private async Task<string> GraphPriv_IsMemberOfGroup(string Parm_AzureUserID, string Parm_GroupID) {
string Lcl_Returnvalue = null;
var Lcl_Uri = "https://graph.windows.net/CleMetroSchools.onmicrosoft.com/users/" + Parm_AzureUserID.Trim() + "/checkMemberGroups?api-version=1.6";
string Lcl_RequestBody = #"{""groupIds"": [""" + Parm_GroupID.Trim() + #"""]}";
Global_HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Global_Token.Result);
HttpResponseMessage Lcl_PostResult = await Global_HttpClient.PostAsync(Lcl_Uri, new StringContent(Lcl_RequestBody, Encoding.UTF8, "application/json"));
if (Lcl_PostResult.Content != null) {
Lcl_Returnvalue = await Lcl_PostResult.Content.ReadAsStringAsync();
}//end if
return Lcl_Returnvalue;
}
The call I use is
if (Graph_IsMemberOfGroup(CurrentUser,Group)){
The problem I am having is that when I Use the same code in a plain (not MVC) ASP.net web application. The system does not wait for GraphPriv_IsMemberOfGroup to completed before it tries to process the if (Lcl_Response.Result != null) {
Anything I have tried so far with waits either will not compile or waits forever.
I have been searching for several days and all I have managed to do is travel deeper down the rabbit hole of confusion.
You're misapplying the async/await model here. You shouldn't be looking for a Task<string>, you should be looking for string from an awaited method:
public async Task<Boolean> Graph_IsMemberOfGroup(string Parm_AzureUserID, string Parm_GroupID) {
Boolean Lcl_ReturnValue = false;
string Lcl_Response = await GraphPriv_IsMemberOfGroup(Parm_AzureUserID, Parm_GroupID);
return Lcl_Response.Result.Contains(Parm_GroupID);
}
An async method returns a value wrapped in a Task<>, The await keyword tells the code to wait for the method to return and unwrap the response. So if an async method returns Task<string> then you would call that method using string s = await method().
I have an issue where i loop over about 31 webservice URLs.
If i put a Thread.Sleep(1000) in the top code, it will work perfectly, but if I remove this, I only get success on 10 (sometimes less and sometimes more) request out of 31. How do I make it wait?
Code
foreach(var item in ss)
{
//Call metaDataApi(url,conn,name,alias)
}
public static void metadataApi(string _url, string _connstring, string _spname, string _alias)
{
// Thread.Sleep(1000);
//Metadata creation - Table Creation
using (var httpClient = new HttpClient())
{
string url = _url;
using (HttpResponseMessage response = httpClient.GetAsync(url).GetAwaiter().GetResult())
using (HttpContent content = response.Content)
{
Console.WriteLine("CHECKING");
if (response.IsSuccessStatusCode)
{
Console.WriteLine("IS OK");
string json = content.ReadAsStringAsync().GetAwaiter().GetResult();
//Doing some stuff not relevant
}
}
}
}
How it can look
You should look to use async/await where you can, but you could try something like this:
// you should share this for connection pooling
public static HttpClient = new HttpClient();
public static void Main(string[] args)
{
// build a list of tasks to wait on, then wait
var tasks = ss.Select(x => metadataApi(url, conn, name, alias)).ToArray();
Task.WaitAll(tasks);
}
public static async Task metadataApi(string _url, string _connstring, string _spname, string _alias)
{
string url = _url;
var response = await httpClient.GetAsync(url);
Console.WriteLine("CHECKING");
if (response.IsSuccessStatusCode)
{
Console.WriteLine("IS OK");
string json = await content.ReadAsStringAsync();
//Doing some stuff not relevant
}
}
One thing to note, this will try to run many in parallel. If you need to run them all one after the other, may want to make another async function that waits on each result individually and call that from the Main. .Result is a bit of an antipattern (with modern c# syntax, you can use async on the main function) but for your script it should be "ok", but I'd minimize usage of it (hence why I wouldn't use .Result inside of a loop.
So, I'm really a PHP developer, so no doubt this is a really obvious thing I'm missing.
I seem to be having problem with running async methods - I've tried a couple of ways (using await, using a Task), and I keep getting the above error whenever calling a method that is async..
The function as it stands currently is ...
public async static void deleteCommands(List<int> commandIds)
{
Tebex.logWarning("Async Delete....");
await Task.Run(() =>
{
String url = "http://www.example.com/" + "queue?";
String amp = "";
foreach (int CommandId in commandIds)
{
url = url + amp + "ids[]=" + CommandId;
amp = "&";
}
Tebex.logWarning("DELETE " + url);
var request = WebRequest.Create(url);
request.Method = "DELETE";
request.Headers.Add("APIKey", "myApiKey");
var response = (HttpWebResponse) request.GetResponse();
Tebex.logWarning(response.ToString());
}).ConfigureAwait(false);
}
and I'm calling it from another method (I don't need the response or anything, it's a fire-and-forget method)
try
{
deleteCommands(executedCommands);
executedCommands.Clear();
}
catch (Exception ex)
{
Tebex.logError(ex.ToString());
}
Previously I was using await request.getResponseAsync() but I received the same error -
public async static void deleteCommands(List<int> commandIds)
{
Tebex.logWarning("Async Delete....");
String url = "http://www.example.com/" + "queue?";
String amp = "";
foreach (int CommandId in commandIds)
{
url = url + amp + "ids[]=" + CommandId;
amp = "&";
}
Tebex.logWarning("DELETE " + url);
var request = WebRequest.Create(url);
request.Method = "DELETE";
request.Headers.Add("APIKey", "myApiKey");
await request.GetResponseAsync();
request = null;
}
As I mentioned, I'm probably missing something obvious, but I can't figure out what!
I'm not sure if any of this resolves your exception, but it should be changed regardless.
Instead of returning void you should return Task from deleteCommands. More on that here: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx?f=255&MSPPError=-2147217396
Depending on how your calling method looks, and how you'd like thread-management to function, there are a couple of solutions:
The preferred:
await deleteCommands(executedCommands);
This requires a async method though.
If you really want to use Task.Run (forces a new thread. Considder the load), then use it when calling the method instead:
Task.Run(async () => await deleteCommands(executedCommands));
Additionaly if you have an async method it, and all following calls, should also follow the async pattern. Switch to async WebRequests and use an async logger.
You should not clear the executedCommands list immediately because it is a fire and forget method (list can be cleared before the method finishes execution)
You should use ContinueWith instead
deleteCommands(executedCommands)
.ContinueWith(r => executedCommands.Clear());
Please forgive me for any noobish mistakes seen below, I'm learning some of the concepts I'm attempting to work with.
Problem:
While debugging my app, I was able to call an async function with Task.Start(). I felt that the app was in a working state for the phase I'm in so removed all breakpoints with CTRL + SHIFT + F9.
Once I ran the app with no breakpoints it would fail due to a property not getting populated. Now when I try to debug any breakpoint I set in the async function that handles most of the work is longer hit. It's like it is getting skipped. Can anyone see a reason why GetWowAuctionFileInfo isn't being called?
GetWowAuctionFileInfo is what is not getting called, or at least appears to be not getting called.
Thanks.
Relevant Code
Caller Function
private void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
w.StartTask("FileInfo", "https://us.api.battle.net/wow/auction/data/medivh?locale=en_US&apikey=<guid>");
foreach (string res in w.ReturnedData)
{
textBoxResults.Text += res;
}
}
Called Functions
public void StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
Task t = new Task(() => GetWowAuctionFileInfo(optionalUri));
t.Start();
//Func<string> function = new Func<string>(() => GetWowAuctionFileInfo(optionalUri));
//Task<string> tInfo = Task<string>.Factory.StartNew(() => GetWowAuctionFileInfo(optionalUri));
}
}
}
private async void GetWowAuctionFileInfo(string auctionInfoUri)
{
RealmJSFileCheck realmInfoObject;
List<string> returnValue = new List<string>();
try
{
using (HttpClient client = new HttpClient())
{
for (int attempt = 0; attempt < 3; attempt++)
{
var response = await client.GetAsync(auctionInfoUri);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
realmInfoObject = JsonConvert.DeserializeObject<RealmJSFileCheck>(content);
returnValue = ConvertFileInfoToConsumableList(realmInfoObject);
//returnValue = realmInfoObject.files.ToString();
break;
}
}
}
}
catch (InvalidOperationException iOpEx)
{
//recieved this when an invalid uri was passed in
}
ReturnedData = returnValue;
}
private List<string> ConvertFileInfoToConsumableList(RealmJSFileCheck jsfc)
{
List<string> returnData = new List<string>();
if (jsfc.files.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.Append("File URL: ");
sb.Append(jsfc.files[0].url);
returnData.Add(sb.ToString());
sb = new StringBuilder();
sb.AppendLine("Last Modified: ");
sb.Append(jsfc.files[0].lastModified);
returnData.Add(sb.ToString());
}
else
{
returnData.Add("No File Info Found");
}
return returnData;
}
UPDATE
Thanks again all for the detailed commentary. I've gone through much documentation regarding Task usage and learned a lot in this exercise. I'm marking the answer from #Johnathon as the solution because it provided exactly what I was asking for and provided a very helpful link for more information.
Your GetWowAuctionFileInfo method is an asynchronous method, and you await an async call within it without returning a Task. In general it is bad practice to use async void. Instead, turn your GetWowAuctionFileInfo method into async Task<List<string>> GetWowAuctionFileInfo. This will let you await the GetAsync call, parse the data, and return the collection to the caller without having to use a ReturnObject.
private async Task<List<string>> GetWowAuctionFileInfo(string auctionInfoUri)
{
RealmJSFileCheck realmInfoObject;
List<string> returnValue = new List<string>();
try
{
using (HttpClient client = new HttpClient())
{
for (int attempt = 0; attempt < 3; attempt++)
{
var response = await client.GetAsync(auctionInfoUri);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
realmInfoObject = JsonConvert.DeserializeObject<RealmJSFileCheck>(content);
// You can just return the List<T> now.
return ConvertFileInfoToConsumableList(realmInfoObject);
//returnValue = realmInfoObject.files.ToString();
break;
}
}
}
}
catch (InvalidOperationException iOpEx)
{
//recieved this when an invalid uri was passed in
}
}
Because the method was originally async void, you could not await the calling of it in your buttonTestJSFCHI_Click. Now that we've made it all Task based, you can await it within your event handler. Note that event handlers are generally the only acceptable place to use async void. Any time you are responsible for the creation of the methods, and not constrained by a contract (like event handlers), you should always return a Task on your async methods.
private async void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
List<string> results = await w.StartTask("FileInfo", "https://us.api.battle.net/wow/auction/data/medivh?locale=en_US&apikey=<guid>");
foreach (string res in results)
{
textBoxResults.Text += res;
}
}
public async Task<List<string>> StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
// Since the GetWowAuctionFileInfo now returns Task, we don't need to create a new one. Just await the Task given back to us, and return the given result.
return await GetWowAuctionFileInfo(optionalUri);
}
}
}
The reason you saw the expected result while debugging is because the debug session was slow enough that the async operation completed in time for your code to use it. When running the app outside of the debugger, it runs faster than the async operation could complete, preventing you from seeing the data. Thus the need to await the entire async call stack, so you can prevent further execution from happening down that code-path until you receive all of the desired data.
Microsoft has a good write up on Task based programming, I'd take a read through it to help you understand it some.
EDIT
Just to clarify, when you return a Task<T> on your methods, you will be given the result when you await. For example:
List<string> result = await StartTask();
Even though StartTask returns Task<List<string>>, the await operation will wait for the StartTask() method to complete, and then unwrap the result from the Task<T> object and give you the result back automatically. So don't let the method signature fool you, if you await it, you will be given back the resulting data, and not the actual Task itself. There won't be any need for you to pull the data out of the Task manually.
Because you not waiting for result.
You loop with ReturnedData before it was assigned with data.
I think you don't need to create new Task at all. Make GetWowAuctionFileInfo method properly asynchronous which returns Task.
private async Task GetWowAuctionFileInfo(string auctionInfoUri)
{
// same code
}
Change StartTask to return Task. Because we not awaiting result here we don't need make method asynchronous.
Suggest to change name of this method to LoadData for example, which give more information about what this method does.
public Task StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
return GetWowAuctionFileInfo(optionalUri) // this will return Task
}
}
// if validation fails - return completed task or throw exception
return Task.CompletedTask;
}
Then you can call it in Button_Click event handler
private async void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
await w.StartTask("FileInfo", "yourUrl");
// This line will be executed only after asynchronous methods completes succesfully
// or exception will be thrown
foreach (string res in w.ReturnedData)
{
textBoxResults.Text += res;
}
}
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.