Handle code after task are done - c#

I'm trying to show a waiting symbol while while a ASYNC task are doing.
I'm really new to this, so if there are better ways to implement this code, please enlighten me :)
But, everything works except the hiding of the pictureBox1 after the code are done and there are now result found. In other words, when there are a result, the pictureBox1 are hidden
Here are the method that runs every time a outlook item are opened
private void FormRegion1_FormRegionShowing(object sender, System.EventArgs e)
{
if (this.OutlookItem is Microsoft.Office.Interop.Outlook.MailItem)
{
Microsoft.Office.Interop.Outlook.MailItem item = (Microsoft.Office.Interop.Outlook.MailItem)this.OutlookItem;
getContactByEmail(item);
}
}
This is the method that I implement the wait stuff
public async Task getContactByEmail(Microsoft.Office.Interop.Outlook.MailItem item)
{
pictureBox1.Visible = true;
using (var client = new System.Net.Http.HttpClient())
{
client.BaseAddress = new Uri("http://api.....");
client.DefaultRequestHeaders.Accept.Clear();
HttpResponseMessage response = await client.GetAsync("tools/getContactByEmail?email=" + item.SenderEmailAddress + "&key=1232");
if (response.IsSuccessStatusCode)
{
SimpleContact contact = await response.Content.ReadAsAsync<SimpleContact>();
lblName.Text = contact.Name;
lblMobile.Text = contact.Phone;
}
pictureBox1.Visible = false;
}
}

Posting the code that fixes this so the exception are not raised
if (response.IsSuccessStatusCode)
{
SimpleContact contact = await response.Content.ReadAsAsync<SimpleContact>();
if (contact != null)
{
lblName.Text = contact.Name;
lblMobile.Text = contact.Phone;
}
pictureBox1.Visible = false;
}

In C# method names are always CamelCase and asynchronous methods are always suffixed Async. Just conventions.
You might want to extract the non UI code to another asynchronous method to avoid going back and forth to the UI thread:
private async void FormRegion1_FormRegionShowing(object sender, System.EventArgs e)
{
if (this.OutlookItem is Microsoft.Office.Interop.Outlook.MailItem)
{
Microsoft.Office.Interop.Outlook.MailItem item = (Microsoft.Office.Interop.Outlook.MailItem)this.OutlookItem;
pictureBox1.Visible = true;
var contact = GetContactByEmailAsync(item);
if (contact != null)
{
lblName.Text = contact.Name;
lblMobile.Text = contact.Phone;
}
pictureBox1.Visible = false;
}
}
public async Task<SimpleContact> GetContactByEmailAsync(Microsoft.Office.Interop.Outlook.MailItem item)
{
using (var client = new System.Net.Http.HttpClient())
{
client.BaseAddress = new Uri("http://api.....");
client.DefaultRequestHeaders.Accept.Clear();
HttpResponseMessage response = await client.GetAsync(
"tools/getContactByEmail?email=" + item.SenderEmailAddress + "&key=1232")
.ConfigureAwait(false);
return (response.IsSuccessStatusCode)
? await response.Content.ReadAsAsync<SimpleContact>();
: null;
}
}
Note: Don't forget proper exception handling!!!

Related

how to use await and async with richtextbox?

i tried to make a app to check ebay current watchers. i use following code find ebay watcher count.. it woks fine. then i use await asyncro code to fix UI blocking..
but it is getting invalidoperationexception was unhandled exception.
private async void button1_Click(object sender, EventArgs e)
{
Task<int> task = new Task<int>(counting);
task.Start();
label1.Text = "Please wait";
await task;
}
private int counting()
{
string[] idlist = richTextBox1.Text.Split('\n'); // <= getting exception
// foreach (string id in idlist)
for (int i = 0; i < Convert.ToInt32(idlist.Length); i++)
{
string url = "http://m.ebay.com/itm/" + idlist[i];
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream());
// richTextBox2.Text += sr.ReadToEnd();
string a = sr.ReadToEnd();
sr.Close();
string number = String.Empty;
string pattern = #"(?<=""defaultWatchCount""\s*:\s*)\d+";
string input = a;
foreach (Match m in Regex.Matches(input, pattern))
{
number = m.Value;
}
richTextBox2.Text += number + Environment.NewLine;
}
return 0;
}
I don't have much knowledge about await and async... could anyone plz fix this error?
Trying to access UI controls from a thread different to the one that created it will cause cross thread access violations. So if the goal is to make the web calls on a background thread then you need to separate the background tasks from the UI
The following is a refactor of the original code that takes a collection of ids and using HttpClient to make asynchronous web calls. All the web calls will be done in parallel on separate threads.
static Lazy<HttpClient> client = new Lazy<HttpClient>(() => {
string baseUrl = "http://m.ebay.com/itm/";
var client = new HttpClient() {
BaseAddress = new Uri(baseUrl)
};
return client;
});
private Task<string[]> GetWatchCountsAsync(string[] idlist) {
string pattern = #"(?<=""defaultWatchCount""\s*:\s*)\d+";
var tasks = idlist.Select(async id => {
var input = await client.Value.GetStringAsync(id.Trim());
string number = String.Empty;
foreach (Match m in Regex.Matches(input, pattern)) {
number += m.Value;
}
return number;
});
return Task.WhenAll(tasks);
}
So now in the button click handler you start in the UI thread, offload the requests to other threads and wait for them to complete while not locking the UI thread. Once complete you extract the returned values and pass them on as needed
private async void button1_Click(object sender, EventArgs e) {
label1.Text = "Please wait..."; //UI Thread
var idlist = richTextBox1.Text.Split('\n'); //UI Thread
var numbers = await GetWatchCountsAsync(idlist); //background thread(s) (non-blocking)
richTextBox2.Text += string.Join(Environment.NewLine, numbers); // Back on UI thread
label1.Text = "Done"; //UI Thread
}

C# UI becomes unresponsive during processing in background thread

I am testing the validity of a large list of proxy servers concurrently. During this testing, many exceptions are being raised and caught. Although I am doing the testing in a background thread, my UI becomes unresponsive unless I use a SemaphoreSlim object to control the concurrency.
I know this is a self imposed bottle neck, and when scaling with an even larger list of proxies to test, I was hoping there might be a better way to solve the problem.
private void ValidateProxiesButton_Click(object sender, EventArgs e)
{
new Thread(async () =>
{
Thread.CurrentThread.IsBackground = true;
await ValidateProxiesAsync(proxies, judges, tests, 10);
}).Start();
}
public async Task ValidateProxiesAsync(IEnumerable<Proxy> proxies, IEnumerable<ProxyJudge> judges, IEnumerable<ProxyTest> tests = null, int maxConcurrency = 20)
{
if (proxies.Count() == 0)
{
throw new ArgumentException("Proxy list empty.");
}
foreach (var proxy in proxies)
{
proxy.Status = ProxyStatus.Queued;
}
//Get external IP to check if proxy is anonymous.
var publicIp = await WebUtility.GetPublicIP();
foreach (var judge in judges)
{
judge.Invalidation = publicIp;
}
await ValidateTestsAsync(judges.ToList<IProxyTest>());
var validJudges = judges.ToList<IProxyTest>().GetValidTests();
if (validJudges.Count == 0)
{
throw new ArgumentException("No valid judges found.");
}
if (tests != null)
{
await ValidateTestsAsync(tests.ToList<IProxyTest>());
}
var semaphore = new SemaphoreSlim(maxConcurrency);
var tasks = new List<Task>();
foreach (var proxy in proxies)
{
tasks.Add(Task.Run(async () =>
{
await semaphore.WaitAsync();
proxy.Status = ProxyStatus.Testing;
var isValid = await proxy.TestValidityAsync((IProxyTest)validJudges.GetRandomItem());
proxy.Status = isValid ? ProxyStatus.Valid : ProxyStatus.Invalid;
semaphore.Release();
}));
}
await Task.WhenAll(tasks);
}
Inside proxy.TestValidityAsync method
public async Task<bool> TestValidityAsync(IProxyTest test, int timeoutSeconds = 30)
{
try
{
var req = WebRequest.Create(test.URL);
req.Proxy = new WebProxy(this.ToString());
var respBody = await WebUtility.GetResponseStringAsync(req).TimeoutAfter(new TimeSpan(0, 0, timeoutSeconds));
if (respBody.Contains(test.Validation))
{
return true;
}
else
{
return false;
}
}
catch (Exception)
{
return false;
}
}
So I found a working solution, it is to add the TPL Dataflow NuGet package to my project and then use the TransformBlock class. When I do this, my UI stays very responsive even if I am processing tons of concurrent requests that often throw exceptions. The code below is proof of concept, I will update it when I translate it to work with my project.
Source: Throttling asynchronous tasks
private async void button1_Click(object sender, EventArgs e)
{
var downloader = new TransformBlock<string, WebResponse>(
url => Download(url),
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 200 }
);
var buffer = new BufferBlock<WebResponse>();
downloader.LinkTo(buffer);
var urls = new List<string>();
for (int i = 0; i < 100000; i++)
{
urls.Add($"http://example{i}.com");
}
foreach (var url in urls)
downloader.Post(url);
//or await downloader.SendAsync(url);
downloader.Complete();
await downloader.Completion;
IList<WebResponse> responses;
if (buffer.TryReceiveAll(out responses))
{
//process responses
}
}
private WebResponse Download(string url)
{
WebResponse resp = null;
try
{
var req = WebRequest.Create(url);
resp = req.GetResponse();
}
catch (Exception)
{
}
return resp;
}
}

Different behaviors when instantiated from button or timer in c#

I have a function called getMessages that can be called by a Button click (using the RelayCommand trigger) or that is called in a timer every 15s.
The desired behavior is:
webservice > deserialize answer > system notification > updatelistview > insert localDB
But when the function is called by the timer the updatelistview is not done. Why does this happen if the function is the same and works perfectly in the button command?
CODE:
// Get messages for the logged in user
public async void getMessages()
{
try
{
List<FriendGetMessage> msg = new List<FriendGetMessage>();
var response = await CommunicationWebServices.GetCHAT("users/" + au.idUser + "/get", au.token);
if (response.StatusCode == HttpStatusCode.OK) // If there are messages for me.
{
var aux = await response.Content.ReadAsStringAsync();
IEnumerable<FriendGetMessage> result = JsonConvert.DeserializeObject<IEnumerable<FriendGetMessage>>(aux);
if (result != null)
{
foreach (var m in result)
{
msg.Add(m);
}
//MsgList=msg;
foreach (var f in Friends)
{
if (f.msg == null || f.msg.Count() == 0)
{
f.msg = new ObservableCollection<Messages>();
}
foreach (var mess in msg)
{
if (mess.idUser == f.idUser)
{
Messages mm = new Messages();
mm.received = mess.message;
mm.timestamp = "Received " + mess.serverTimestamp;
mm.align = "Right";
// Add to the friend list.
f.msg.Add(mm);
// Add to Local DB
InsertMessage(null, au.idUser.ToString(), f.idUser, mess.message, mess.serverTimestamp);
var notification = new System.Windows.Forms.NotifyIcon()
{
Visible = true,
Icon = System.Drawing.SystemIcons.Information,
BalloonTipIcon = System.Windows.Forms.ToolTipIcon.Info,
BalloonTipTitle = "New Message from " + f.name,
BalloonTipText = "Message: " + mess.message,
};
// Display for 5 seconds.
notification.ShowBalloonTip(5);
// The notification should be disposed when you don't need it anymore,
// but doing so will immediately close the balloon if it's visible.
notification.Dispose();
}
}
}
counterChat = 1; // resets the counter
}
}
else {
counterChat = counterChat * 2;
}
//var sql = "select * from chat";
//var respo = GetFromDatabase(sql);
OnPropertyChanged("Friends");
}
catch (Exception e)
{
MessageBox.Show("GetMessages: " + e);
Debug.WriteLine("{0} Exception caught.", e);
}
}
CODE TIMER:
public void chatUpdate()
{
_timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
}
public void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
getMessages();
OnPropertyChanged("Friends");
incChat = 0;
}
}
ADDED - I've also tried this and didn't worked (it seems that is some kind of concurrency problem to the ObservableCollection called Friends (is a friendslist) each friend has an ObservableCollection of messages (is a chat))
public void chatUpdate()
{
_timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
}
public async void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
Application.Current.Dispatcher.Invoke((Action)async delegate { await getMessages(); });
incChat = 0;
}
}
Best regards,
I think you need to make the timer handler be an async method as follows:
public async void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
await getMessages();
OnPropertyChanged("Friends");
incChat = 0;
}
}
This way OnPropertyChanged("Friends") is guaranteed to fire after the work in getMessages is done.
The methods need to change to:
DispatcherTimer _timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
public async void timerchat_Tick(object sender, EventArgs e)
{
//...
await getMessages();
//...
}
public async Task getMessages()
{
try
{
// ... your code here
string result = await response.Content.ReadAsStringAsync();
// .... rest of your code
}
catch (Exception e)
{
MessageBox.Show("GetMessages: " + e);
}
}
It is solved. The problem was in my ViewModels I was opening multiple threads and sometimes the right one would update the UI and sometimes no.
Thanks for all the answers.

http request not working in nested async Task<T> methods

I'm working with async tasks for the first time, and I'm confronted with a problem I can't seem to solve.
i've got a method to call an API using a Http request.
internal static async Task<HttpResponse> CallAPI(string objectname, string parameters, HttpMethod method)
{
HttpResponse r = new HttpResponse();
using (r.HttpClient = new HttpClient())
{
r.HttpClient.BaseAddress = new Uri("https://" + Properties.Settings.Default.APIURL + "/");
r.HttpClient.DefaultRequestHeaders.Clear();
r.HttpClient.DefaultRequestHeaders.Add("Accept", "application/vnd+json;version=1");
r.HttpClient.DefaultRequestHeaders.Add("Host", Properties.Settings.Default.APIURL);
r.HttpClient.DefaultRequestHeaders.Add("Expect", "100-continue");
r.HttpClient.DefaultRequestHeaders.Add("Connection", "Close");
switch (method)
{
case HttpMethod.DELETE:
using (r.ResponseMessage = await r.HttpClient.DeleteAsync("api/" + objectname.ToString() + "/" + parameters))
{
var stopwatch = Stopwatch.StartNew();
r.responseTime = Convert.ToInt32(stopwatch.ElapsedMilliseconds);
r.ResponseData = await r.ResponseMessage.Content.ReadAsStringAsync();
return r;
}
case HttpMethod.GET:
using (r.ResponseMessage = await r.HttpClient.GetAsync("api/" + objectname.ToString() + parameters))
{
var stopwatch = Stopwatch.StartNew();
r.responseTime = Convert.ToInt32(stopwatch.ElapsedMilliseconds);
r.ResponseData = await r.ResponseMessage.Content.ReadAsStringAsync();
return r;
}
default: throw new Exception("No HTTP Method Found");
}
}
I call a delete() method in the class from a button click event:
protected void btnDelete_Click(object sender, EventArgs e)
{
Activity a = new Activity();
a.Id = Convert.ToInt32(txtObjectId.text);
//a.Delete(); //void method
bool done = a.Delete().Result; //Task<bool> method
}
if I make the delete() method a void, it works fine and returns the http response
public async virtual void Delete()
{
HttpResponse r = new HttpResponse();
r = await CallAPI(_Objectname.ToString(), _Id.ToString(), HttpMethod.DELETE);
}
but if I try to make Delete() a Task,
public async virtual Task<bool> Delete()
{
try
{
HttpResponse r = new HttpResponse();
r = await CallAPI(_Objectname.ToString(), _Id.ToString(), HttpMethod.DELETE);
return true;
}
catch
{
return false;
}
}
it tries to execute the httpclient.deleteasync method and the app just doens't do anything. I don't get an exception, it doens't freeze, it just doens't seem to do anything anymore.
I have no idea what's causing this behavior, but I'm fairly new to async programming, so I'm probably doing something which I'm not supposed to :-)
If you do .Result or .Wait() on code that uses async/await you will deadlock your program, you must make your event handler async void, this is the only place you are allowed to use async void.
protected async void btnDelete_Click(object sender, EventArgs e)
{
Activity a = new Activity();
a.Id = Convert.ToInt32(txtObjectId.text);
//await a.Delete(); //Task method
bool done = await a.Delete(); //Task<bool> method
}
And in case you do end up using the version that does not return Task<bool> the Delete() function would look like
public async virtual Task Delete()
{
HttpResponse r = new HttpResponse();
r = await CallAPI(_Objectname.ToString(), _Id.ToString(), HttpMethod.DELETE);
}
you do not need a return statement.
Also, if you don't need to access the UI, add .ConfigurateAwait(false) to each await call to make it not force the UI thread for it's continuations.
public async virtual Task Delete()
{
HttpResponse r = new HttpResponse();
r = await CallAPI(_Objectname.ToString(), _Id.ToString(), HttpMethod.DELETE).ConfigureAwait(false);
}
You can do the same to CallAPI too to make it run better.

DataGridView Update/Refresh issue

I have a windows form which has the following code
BindingList<TicketResult> tickResults = new BindingList<TicketResult>();
BindingSource bindingSource1 = new BindingSource();
Action<String> call;
private void method(String x)
{
if (this.dataGridView1.InvokeRequired)
{
lock (this)
{
dataGridView1.Invoke(
new MethodInvoker(() =>
{
Debug.WriteLine(x);
tickResults[int.Parse(x)].Row = "first page";
dataGridView1.Refresh();
}));
}
}
}
public Form1()
{
call = method;
ServicePointManager.DefaultConnectionLimit = 48;
InitializeComponent();
tickResults.ListChanged += tickResults_ListChanged;
for (int i = 0; i < 10; i++)
{
TicketResult result = new TicketResult();
tickResults.Add(result);
}
bindingSource1.DataSource = tickResults;
dataGridView1.DataSource = bindingSource1;
for (int i = 0; i < 10; i++)
{
Search s = new Search();
int x = i;
Task.Run(() => s.start(x, this.call));
}
}
I don't understand why the change in tickResults is not reflected without calling dataGridView1's Refresh() method.
Code for other classes which call the "call" delegate in the form are as follows:
class Search : ISearch
{
public async Task<bool> start(int i, Action<String> x)
{
bool result = false;
TicketLogic tixLogic = new TicketLogic();
try
{
await Task.Run(() => tixLogic.processFirstPage(i, x))
.ContinueWith((t) => tixLogic.processSecondPage(i, x))
.ContinueWith((t) => tixLogic.processThirdPage(i, x));
result = true;
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
result = false;
}
return result;
}
public async Task<bool> stop()
{
return false;
}
public async Task<bool> restart()
{
return false;
}
}
class TicketLogic
{
public async Task<bool> processFirstPage(int i, Action<String> x)
{
bool result = false;
try
{
HttpWebRequest request = WebRequest.CreateHttp("http://www.google.com");
WebResponse response = await request.GetResponseAsync();
StreamReader reader = new StreamReader(response.GetResponseStream());
String textResponse = await reader.ReadToEndAsync();
reader.Close();
response.Close();
result = true;
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
result = false;
}
return result;
}
public async Task<bool> processSecondPage(int i, Action<String> x)
{
bool result = false;
try
{
HttpWebRequest request = WebRequest.CreateHttp("http://www.example.com");
WebResponse response = await request.GetResponseAsync();
StreamReader reader = new StreamReader(response.GetResponseStream());
String textResponse = await reader.ReadToEndAsync();
//tixResult.Information = "Second Page";
reader.Close();
response.Close();
x(i.ToString());
result = true;
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
result = false;
}
return result;
}
public async Task<bool> processThirdPage(int i, Action<String> x)
{
bool result = false;
try
{
HttpWebRequest request = WebRequest.CreateHttp("http://www.hotmail.com");
WebResponse response = await request.GetResponseAsync();
StreamReader reader = new StreamReader(response.GetResponseStream());
String textResponse = await reader.ReadToEndAsync();
//tixResult.Information = "Third Page";
reader.Close();
response.Close();
x(i.ToString());
result = true;
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
result = false;
}
return result;
}
}
Before this I tried one more approach, in which I was passing the databound object to a computation Task, where the databound object got manipulated, but even there the result was the same i.e. the changes in the object were not reflected upon the grid until I clicked some Cell in the Grid or minimized/maximized the form.
My question is, why are the changes not being reflected in the Grid without calling datagrid refresh() ??
Try using ObservableCollection instead of BindingList
The Observable implements the INotifyPropertyChange which notifies the DataGridView when something changes
It is often difficult to answer question like this as we don't usually have access to the reasoning of the .NET designers.
So I'll try to make sense of it by guessing; I hope this not just helps you to understand but also to accept and make the best of it..
Maybe the powers that be decided that constant automatic refreshing is not good for e.g. performance or even user experience. So they leave it to you to decide just when all updates are through to to trigger a Refresh..
There is a big difference between a Click and calls from code, let alone from other tasks. A Click happens on the UI, so the UI should be current. Changes to the datasource from code could happen multiple times in a row, at any frequency and a flickering UI would not be nice..
Let's try to change the perspective: Instead of seeing the issue at hand as a tedious extra task one could see it as a chance to control the times when the refresh happens.
Or to change the perspective even further: You can prevent the user from being flooded with updates when he maybe really would prefer a Refresh button and maybe an unobtrusive count of outstanding changes or new records..

Categories

Resources