ObjectDisposedException after second async call - c#

I'm trying to send a HTTP POST request multiple times but not in parallel. I've created another class and put this function into it:
public async Task<string> sendReqAsync()
{
requestTime = DateTime.Now;
var task = await client.PostAsync("https://api.example.com", content).ContinueWith(async (result) =>
{
responseTime = DateTime.Now;
return await result.Result.Content.ReadAsStringAsync();
});
return task.Result;
}
And finally it'll be called in an click event for a button:
private async void ManualSendBtn_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
// Here "client" is object of my class that have been initilized in the main WPF function
await client.sendReqAsync().ContinueWith((result) =>
{
var parsedResponse = JObject.Parse(result.Result);
this.Dispatcher.Invoke(() =>
{
ordersListView.Items.Add(new OrderDetailsItem
{
IsSuccessful = bool.Parse(parsedResponse["IsSuccessfull"].ToString()),
ServerMessage = parsedResponse["MessageDesc"].ToString(),
RequestTime = client.requestTime.ToString("hh:mm:ss.fff"),
ResponseTime = client.responseTime.ToString("hh:mm:ss.fff"),
ServerLatency = (client.responseTime - client.requestTime).Milliseconds.ToString()
});
});
});
}
But it works just for the first click (event occurs) and for next click it gives me:
Inner Exception 1: AggregateException: One or more errors occurred.
Inner Exception 2: AggregateException: One or more errors occurred.
Inner Exception 3: ObjectDisposedException: Cannot access a disposed
object.
Edit: Below is the edited code as suggested, but the error is not fixed.
MyHTTPClient
public class MyHTTPClient
{
private static readonly HttpClient client = new HttpClient();
StringContent content;
public DateTime requestTime;
public DateTime responseTime;
public void configureReq(string oauthToken)
{
var postBodyJSONObject = new
{
body = "value"
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", oauthToken);
content = new StringContent(JsonConvert.SerializeObject(postBodyJSONObject), Encoding.UTF8, "application/json");
}
public async Task<string> sendReqAsync()
{
requestTime = DateTime.Now;
var result = await client.PostAsync("https://example.com", content);
responseTime = DateTime.Now;
return await result.Content.ReadAsStringAsync();
}
}
Also here is my window initializer function:
MyHTTPClient client; // Somewhere in the class
public MainWindow() {
client = new MyHTTPClient();
client.configureReq(AUTHORIZATION);
}

First of all: You shouldn't mix different asynchronous models of programming.
In your code you are trying to combine async/await with legacy ContinueWith approach.
You can read more about async/await approach here and about async/await and ContinueWith differences here (Thanks to MickyD for leaving link to this in the comments).
So to cut a long story short your code should look like this:
public async Task<string> SendRequestAsync()
{
requestTime = DateTime.Now;
var response = await client.PostAsync("api.example.com", content);
var responseTime = DateTime.Now;
return await response.Content.ReadAsStringAsync();
}
Note that now code looks much more like synchronous and is a way readable than callbacks with ContinueWith.
You can find more about asynchronous programming in C# on MSDN. I suggest you to read and try to exectue some code in console app before writing it in yourmain app.

In addition to fixing the SendRequestAsync method, as shown by Neistow's answer, you must fix the button's click handler too:
private async void ManualSendBtn_PreviewMouseLeftButtonUp(object sender,
MouseButtonEventArgs e)
{
string response = await client.SendRequestAsync();
var parsedResponse = JObject.Parse(response);
ordersListView.Items.Add(new OrderDetailsItem
{
IsSuccessful = bool.Parse(parsedResponse["IsSuccessfull"].ToString()),
ServerMessage = parsedResponse["MessageDesc"].ToString(),
RequestTime = client.RequestTime.ToString("hh:mm:ss.fff"),
ResponseTime = client.ResponseTime.ToString("hh:mm:ss.fff"),
ServerLatency = (client.ResponseTime - client.RequestTime).Milliseconds.ToString()
});
}
There is no reason to mess with the Dispatcher.Invoke. The code after the await will continue running on the UI thread, as it happens by default in all Windows Forms and WPF applications.

Related

Using MessageBox when part of an await has ConfigureAwait false

Reading Stephen Cleary take on not blocking on Async code I write something like this
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
return JObject.Parse(jsonString);
}
}
public async void Button1_Click(...)
{
var json = await GetJsonAsync(...);
textBox1.Text=json;
}
so far so good, I understand that after the ConfigureAwait the method is going to continue running on a different context after GetStringAsync returns.
but what about if I want to use something like MessageBox (which is UI) like this
public static async Task<JObject> GetJsonAsync(Uri uri)
{
if(someValue<MAXVALUE)
{
using (var client = new HttpClient())
{
//var jsonString = await client.GetStringAsync(uri); //starts the REST request
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
return JObject.Parse(jsonString);
}
}
else
{
MessageBox.Show("The parameter someValue is too big!");
}
}
can I do this?
Even more complicated, how about this?
public static async Task<JObject> GetJsonAsync(Uri uri)
{
if(someValue<MAXVALUE)
{
try{
using (var client = new HttpClient())
{
//var jsonString = await client.GetStringAsync(uri); //starts the REST request
var jsonString = await client.GetStringAsync(uri).ConfigureAwait(false);
return JObject.Parse(jsonString);
}
}
catch(Exception ex)
{
MessageBox.Show("An Exception was raised!");
}
}
else
{
MessageBox.Show("The parameter someValue is too big!");
}
}
Can I do this?
Now, I am thinking perhaps all the message boxes should be called outside GetJsonAync as good design, but my question is can the above thing be done?
can I do this? [use a MessageBox]
Yes, but mainly because it has nothing to do with async/await or threading.
MessageBox.Show() is special, it is a static method and is documented as thread-safe.
You can show a MessageBox from any thread, any time.
So maybe it was the wrong example, but you do have MessageBox in the title.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
try{
... // old context
... await client.GetStringAsync(uri).ConfigureAwait(false);
... // new context
}
catch
{
// this might bomb
someLabel.Text = "An Exception was raised!";
}
}
In this example, there could be code paths where the catch runs on the old and other paths where it runs on the new context.
Bottom line is: you don't know and should assume the worst case.
I would not use a Message Box, as it is very limited, and dated.
Also, Pop up's are annoying.
Use your own user control which enables user interaction the way you intend it.
In the context of Winforms / WPF / (and I guess UWP), only a single thread can manipulate the UI. Other threads can issue work to it via a queue of actions which eventually get invoked.
This architecture prevents other threads from constantly poking at the UI, which can make UX very janky (and thread unsafe).
The only way to communicate with it the UI work queue (in Winforms) is via the System.Windows.Form.Controls.BeginInvoke instance method, found on every form and control.
In your case:
public async void Button1_Click(...)
{
var json = await GetJsonAsync(...).ConfigureAwait(false);
BeginInvoke(UpdateTextBox, json);
}
private void UpdateTextBox(string value)
{
textBox1.Text=json;
}

Is this a correct use of async void? Xamarin

I'm testing the waters of Xamarin and trying to convert an android project I have to it. I have a very basic login using facebook. My code looks like this:
public class MainActivity : Activity, IFacebookCallback
{
private LoginButton loginButton;
ICallbackManager callbackManager;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
loginButton = FindViewById<LoginButton>(Resource.Id.login_button);
loginButton.SetReadPermissions(new List<string>() {"email"});
this.callbackManager = CallbackManagerFactory.Create();
loginButton.RegisterCallback(callbackManager, this);
}
public async void OnSuccess(Java.Lang.Object result)
{
var loginResult = result as LoginResult;
var accesstoken = loginResult.AccessToken;
var client = new FacebookApiClient();
var textview = FindViewById<TextView>(Resource.Id.emailDisplay);
try
{
var fbResponse = await GetFaceBookResponse(accesstoken);
textview.Text = fbResponse.Email;
}
catch(System.Exception ex)
{
var myException = ex;
}
}
private async Task<FacebookResponse> GetFaceBookResponse(AccessToken accessToken)
{
var client = new FacebookApiClient();
var response = await client.GetEmail(accessToken);
return response;
}
}
So I'm implementing the OnSuccess method in IFacebookCallback, which requires a void method. This appears to be the only way to do this, which seems ok to me and also appears to be working fine. However, I know it's a no-no to use async with a void return type, except in special situations (event handlers). Just double checking here to make sure this won't cause any problems?
Also, if there is a better way to do this, what is it? While I was googling I found a way using Task.Run:
var cts = new CancellationTokenSource();
var ct = cts.Token;
try
{
var fbResponse = await Task.Run(async () =>
{
var response = await client.GetEmail(accesstoken);
return response;
}, ct);
textview.Text = fbResponse.Email;
}
But I'm not really sure why that would be better than what I'm doing, or if it's actually really any different.
So I'm implementing the OnSuccess method in IFacebookCallback, which requires a void method. This appears to be the only way to do this, which seems ok to me and also appears to be working fine. However, I know it's a no-no to use async with a void return type, except in special situations (event handlers). Just double checking here to make sure this won't cause any problems?
That's exactly what you're using it for, so it's fine. As long as you're not trying to await that method anywhere in your own code (since async void methods can't be awaited, which is the reason why they're a no-no), you're good.
One of Microsoft's samples also contains a simple async implementation of IFacebookCallback.OnSuccess(), here.

Parallel.For and httpclient crash the application C#

I want to avoid application crashing problem due to parallel for loop and httpclient but I am unable to apply solutions that are provided elsewhere on the web due to my limited knowledge of programming. My code is pasted below.
class Program
{
public static List<string> words = new List<string>();
public static int count = 0;
public static string output = "";
private static HttpClient Client = new HttpClient();
public static void Main(string[] args)
{
//input path strings...
List<string> links = new List<string>();
links.AddRange(File.ReadAllLines(input));
List<string> longList = new List<string>(File.ReadAllLines(#"a.txt"));
words.AddRange(File.ReadAllLines(output1));
System.Net.ServicePointManager.DefaultConnectionLimit = 8;
count = longList.Count;
//for (int i = 0; i < longList.Count; i++)
Task.Run(() => Parallel.For(0, longList.Count, new ParallelOptions { MaxDegreeOfParallelism = 5 }, (i, loopState) =>
{
Console.WriteLine(i);
string link = #"some link" + longList[i] + "/";
try
{
if (!links.Contains(link))
{
Task.Run(async () => { await Download(link); }).Wait();
}
}
catch (System.Exception e)
{
}
}));
//}
}
public static async Task Download(string link)
{
HtmlAgilityPack.HtmlDocument document = new HtmlDocument();
document.LoadHtml(await getURL(link));
//...stuff with html agility pack
}
public static async Task<string> getURL(string link)
{
string result = "";
HttpResponseMessage response = await Client.GetAsync(link);
Console.WriteLine(response.StatusCode);
if(response.IsSuccessStatusCode)
{
HttpContent content = response.Content;
var bytes = await response.Content.ReadAsByteArrayAsync();
result = Encoding.UTF8.GetString(bytes);
}
return result;
}
}
There are solutions for example this one, but I don't know how to put await keyword in my main method, and currently the program simply exits due to its absence before Task.Run(). As you can see I have already applied a workaround regarding async Download() method to call it in main method.
I have also doubts regarding the use of same instance of httpclient in different parallel threads. Please advise me whether I should create new instance of httpclient each time.
You're right that you have to block tasks somewhere in a console application, otherwise the program will just exit before it's complete. But you're doing this more than you need to. Aim for just blocking the main thread and delegating the rest to an async method. A good practice is to create a method with a signature like private async Task MainAsyc(args), put the "guts" of your program logic there, call it from Main like this:
MainAsync(args).Wait();
In your example, move everything from Main to MainAsync. Then you're free to use await as much as you want. Task.Run and Parallel.For are explicitly consuming new threads for I/O bound work, which is unnecessary in the async world. Use Task.WhenAll instead. The last part of your MainAsync method should end up looking something like this:
await Task.WhenAll(longList.Select(async s => {
Console.WriteLine(i);
string link = #"some link" + s + "/";
try
{
if (!links.Contains(link))
{
await Download(link);
}
}
catch (System.Exception e)
{
}
}));
There is one little wrinkle here though. Your example is throttling the parallelism at 5. If you find you still need this, TPL Dataflow is a great library for throttled parallelism in the async world. Here's a simple example.
Regarding HttpClient, using a single instance across threads is completely safe and highly encouraged.

Working with WCF and task-based operations

I'm trying to make it possible for a client to make several searches at the same time without it affecting the response time of the seach page. To do this i was hoping to use .NET 4.5's task-based operations in WCF but I'm having troubles understanding the logic.
What I want my end result to be is to do multiple calls to the WCF API and then merge these into one big result and order it by each search items relevence.
This is how my code looks like today:
public partial class WebForm1 : System.Web.UI.Page
{
private static SwepubSearchServiceClient _client;
private static List<SearchItem> _searchItems = new List<SearchItem>();
protected void Page_Load(object sender, EventArgs e)
{
_client = new SwepubSearchServiceClient();
var result1 = GetAsyncSearchItems("query1", "subQuery1");
var result2 = GetAsyncSearchItems("query2", "subQuery2");
var result3 = GetAsyncSearchItems("query3", "subQuery3");
// What to do here?
}
private static async Task<SearchItem[]> GetAsyncSearchItems(string query = "", string subQuery = "")
{
var task = _client.DoSearchSimpleAsync(query, subQuery);
return await task;
}
}
Basicly, what I need to know is what to do with each result (result1, result2, result3) after i get them back. I though i was going to be able to call result1.Result after each method call but by then they aren't yet computed so it won't do any good.
While I was trying stuff before my GetAsyncSearchItems method looked something like this:
private static async void GetAsyncSearchItems()
{
var task1 = _client.DoSearchSimpleAsync("query1", "subQuery1");
var task2 = _client.DoSearchSimpleAsync("query2", "subQuery2");
var task3 = _client.DoSearchSimpleAsync("query3", "subQuery3");
var result1 = await task1;
var date1 = DateTime.Now.TimeOfDay;
var result2 = await task2;
var date2 = DateTime.Now.TimeOfDay;
var result3 = await task3;
var date3 = DateTime.Now.TimeOfDay;
var list = new List<SearchItem>();
list.AddRange(result1);
list.AddRange(result2);
list.AddRange(result3);
}
When i did this i was able to retrieve the result in the list object but I wasn't able to return it to my Page_Load since async method can only return void, Task or Task so it was of no use.
Or am I misunderstanding something? Should I work inside the static method with the result?
I'm probobly missing something basic here but I'm hoping someone can stear me in the right direction. I would really apreachiate it!
You can do it like this:
var task1 = _client.DoSearchSimpleAsync("query1", "subQuery1");
var task2 = _client.DoSearchSimpleAsync("query2", "subQuery2");
var task3 = _client.DoSearchSimpleAsync("query3", "subQuery3");
await Task.WhenAll(task1, task2, task3);
var result1 = task1.Result;
var date1 = DateTime.Now.TimeOfDay;
var result2 = task2.Result;
var date2 = DateTime.Now.TimeOfDay;
var result3 = task3.Result;
var date3 = DateTime.Now.TimeOfDay;
The ".NET 4.5 way" of doing it, as discussed in this Q&A, is to make your Page_Load method async. Then you can call your async method, and ASP.NET will do the rest towards the client:
protected async void Page_Load(object sender, EventArgs e)
{
...
var results = await GetAsyncSearchItems(...)
... do something with the results
}
private static async SearchItem[] GetAsyncSearchItems()
{
... await stuff
return list;
}
More resources:
The Magic of using Asynchronous Methods in ASP.NET 4.5 plus an important gotcha - blog post by Scott Hanselman
Here's a video tutorial on Async in ASP.NET given in that answer I linked to.
You need to add your three results, result1, result2 and result3 to an array of Tasks.
Then you can do this:
Task.WaitAll(tasks);
where tasks is the array.
http://msdn.microsoft.com/en-us/library/dd270695(v=vs.110).aspx
After the WaitAll completes, you can retrieve the lists of SearchItems from each task.
Your GetAsyncSearchItems method should look like this (following the Task-based Asynchronous Pattern):
private static async Task<List<SearchItem>> GetSearchItemsAsync()
{
var task1 = _client.DoSearchSimpleAsync("query1", "subQuery1");
var task2 = _client.DoSearchSimpleAsync("query2", "subQuery2");
var task3 = _client.DoSearchSimpleAsync("query3", "subQuery3");
var results = await Task.WhenAll(task1, task2, task3);
var list = new List<SearchItem>();
list.AddRange(results[0]);
list.AddRange(results[1]);
list.AddRange(results[2]);
return list;
}
Once you have that method, you can write Page_Load as such:
protected async void Page_Load(object sender, EventArgs e)
{
_client = new SwepubSearchServiceClient();
var results = await GetSearchItemsAsync();
...
}
While async void works in this scenario, the ASP.NET team prefers you use RegisterAsyncTask, as such:
protected void Page_Load(object sender, EventArgs e)
{
_client = new SwepubSearchServiceClient();
RegisterAsyncTask(new PageAsyncTask(PageLoadAsync));
}
private async Task PageLoadAsync()
{
var results = await GetSearchItemsAsync();
...
}
You may find my async intro helpful.

Refresh DataServiceCollection

I wonder if there is a code there that refresh my DataServiceCollection that is loaded in a WPF client using BeginExecute async await Task as show below:
public static async Task<IEnumerable<TResult>> ExecuteAsync<TResult>(this DataServiceQuery<TResult> query)
{
//Thread.Sleep(10000);
var queryTask = Task.Factory.FromAsync<IEnumerable<TResult>>(query.BeginExecute(null, null), (asResult) =>
{
var result = query.EndExecute(asResult).ToList();
return result;
});
return await queryTask;
}
I call the extension method as the following:
public async void LoadData()
{
_ctx = new TheContext(the URI);
_dataSource = new DataServiceCollection<TheEntity>(_ctx);
var query = _ctx.TheEntity;
var data = await Task.Run(async () => await query.ExecuteAsync());
_dataSource.Clear(true);
_dataSource.Load(data);
}
LoadData is Called in a ViewModel
Change a field value by hand using SQL Management Studio
Call LoadData again <<<< Does not refresh!!!
Meanwhile, If I use Load method the data is refreshed without any problems as:
var query = _ctx.TheEntity;
_dataSource.Load(query);
Another issue is that I do not know how to Cancel client changes. Last question is whether the MergeOption has any effect to BeginExecute..EndExecute or it only works with Load method?
I suspect that the data context does not like being accessed from multiple threads.
I recommend that you first remove all processing from your extension method:
public static Task<IEnumerable<TResult>> ExecuteAsync<TResult>(this DataServiceQuery<TResult> query)
{
return Task.Factory.FromAsync(query.BeginExecute, query.EndExecute, null);
}
Which you should use without jumping onto a background thread:
public async Task LoadDataAsync()
{
_ctx = new TheContext(the URI);
_dataSource = new DataServiceCollection<TheEntity>(_ctx);
var query = _ctx.TheEntity;
var data = await query.ExecuteAsync();
_dataSource.Clear(true);
_dataSource.Load(data);
}

Categories

Resources