catch exception from awaited async function - c#

I got the following:
public async Task<bool> IsAuthenticatedAsync()
{
using (HttpClient client = new HttpClient())
{
using (MultipartFormDataContent content = new MultipartFormDataContent())
{
content.Add(new StringContent(this.Username, Encoding.UTF8), "username");
content.Add(new StringContent(this.Password, Encoding.UTF8), "password");
try
{
HttpResponseMessage message = await client.PostAsync(authenticatedUrl, content);
if (message.StatusCode == HttpStatusCode.Accepted)
return true;
return false;
}
catch(HttpRequestException)
{
System.Windows.Forms.MessageBox.Show("Some text.");
}
}
}
return false;
}
where authenticatedUrl is some static Uri.
Now assuming that the webserver is not available (or the adress is wrong) await client.PostAsync(authenticatedUrl, content) throws a HttpRequestException, hence the try-catch.
Problem is that the exception is not getting caught. I tried turning off Just My Code but that just adds other exceptions (i.e. SocketException) as suggested here and still doesn't let catch handle the exception.
Why isn't the exception being caught?
Edit
main form(GUI):
public GUI(...)
{
...
CheckLoginState(username, password);
...
}
private async void CheckLoginState(string username, string password)
{
User user = new User(username, password);
if (user.Username != null && user.Password != null && await user.IsAuthenticatedAsync())
abmeldenToolStripMenuItem.Enabled = true;
}

I'm pretty sure the exception is not being caught, because it is wrapped in the task returned from the call to "CheckLoginState"-method, and since you are not awaiting that task, then your exception will never get thrown. From the looks of it, you are calling it from a constructor so I'm going to assume you can't make the "public GUI" into an "public async void GUI".
For the sake of testing, you could try a blocking wait ex:
public GUI(...)
{
...
var task = CheckLoginState(username, password).Wait();
if(task.IsFaulted && task.Exception != null)
{
throw task.Exception
}
...
}
Another way would be to let the async/await-pattern to spread naturally and tie it up to an event (Don't know if you're using wpf or winforms, so I'll assume wpf).
public GUI(...)
{
this.Loaded += async (obj, eventArgs) => await CheckLoginState(username, password);
...
}
Of course, it doesn't have to be the "loaded" event. Point is, that an event can be async, a constructor can't (from what I know anyway).

Related

taskcanceledexception a task was canceled

I am receiving error
taskcanceledexception a task was canceled without any inner exception details, and I am not receiving taskcanceled exception in Sentry. How can I see what the stack trace for this exception is or what changes I need to make to the code ?
Thanks
private T CallDiffbotAndDeserialise<T>(string baseUrl, string pageUrl, int maxTags, int minimumTagConfidencePercentage)
{
var client = diffBotConnection.GetClient();
client.BaseAddress = new Uri(baseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
HttpResponseMessage response = client.GetAsync($"?token={settings.DiffBotToken}&maxTags={maxTags}&tagConfidence={minimumTagConfidencePercentage / 100}&url={Uri.EscapeDataString(pageUrl)}&ts={DateTime.Now.ToSafeCacheString()}").Result;
string responseString = response.Content.ReadAsStringAsync().Result;
T diffBotResponse = JsonConvert.DeserializeObject<T>(responseString);
return diffBotResponse;
}
catch (AggregateException e) // If the task is cancelled or times out
{
return default(T);
};
}
API connection:
public abstract class APIConnection : IDisposable
{
protected HttpClient Client;
private bool disposed = false;
protected APIConnection() : this(3000) { }
protected APIConnection(int timeOut)
{
Client = new HttpClient()
{
Timeout = TimeSpan.FromMilliseconds(timeOut)
};
}
public HttpClient GetClient() => Client;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
Client.Dispose();
}
disposed = true;
}
}
You are calling .Result which always throws AggregateException.
That means you are not only catching TaskCancelledException or OperationCancelledException, you'll catch anything thrown by the two calls to .Result.
Since you are handling the exception and hiding the fact it ever happened (by catch and returning) Sentry won't know about it. If you want to send that event to Sentry, you'd need to call the Sentry client manually.
With SharpRaven:
var ravenClient = new RavenClient("dsn"); // Initialize the client
ravenClient.CaptureEvent(new SentryEvent(exception));
With the new SDK Sentry is developing (which is still a preview release):
// Initialize the SDK only once, at the start of the app
using (SentrySdk.Init("dsn"))
{
SentrySdk.AddBreadcrumb($"Starting a web request to: {baseUrl}");
try
{
// make request
}
catch (Exception e)
{
SentrySdk.CaptureException(exception);
}
}
In this example I added a breadcrumb which in case of an event (for example capturing an exception explicitly like above) is sent together with the event.
Also note that the new SDK automatically detects exceptions that go unhandled. That is not the case of your exemple since you are explicitly catching it.
I think it's important to mention that ideally you would avoid blocking the thread by calling .Result and instead would use async/await.
The await keyword unwraps the Exception from the faulted Task.
That means that your catch block could now catch OperationCancelledException instead. Any other error like a total failure to connect to the server would not go into your catch block and instead would bubble up the stack.

Show AlertDialog on HttpClient exception (Xamarin Android)

My Xamarin Android app utilizes a Web service, which it connects to using HttpClient. On no connection (for exmaple when the user has no cell nor WiFi connection), an excpetion is thrown. I'm using async/await to get the data from the server. Here is an excerpt from my code:
public async Task<String> doLogin(string username, string password)
{
String url = Constants.loginEndpoint + username + "/" + password + "/";
var uri = new Uri(string.Format(url, string.Empty));
return_string = "";
try
{
var response = await GetAsync(uri);
if (response.IsSuccessStatusCode)
{
return_string = "success";
// Process the positive response here
else
{ }
}
catch (Exception ex)
{
throw new ConnectionException();
}
return return_string;
}
I defined a custon ConnectionException and want to show an AlertDialog to the user to inform them, that the request failed due to no connection. After the user clicks OK I want to close the app. I tried to show the alert dialog in the following way, but it's not working:
public class ConnectionException : Exception
{
public ConnectionException()
{
AlertDialog.Builder alert = new AlertDialog.Builder(myApp.Context);
alert.SetTitle("Failure");
alert.SetMessage("Request failed. No connection.");
alert.SetPositiveButton("OK", (senderAlert, args) =>
{
});
Dialog dialog = alert.Create();
dialog.Show();
}
public ConnectionException(string message)
: base(message)
{ }
public ConnectionException(string message, Exception innerException)
: base(message, innerException)
{ }
}
Is this the right approach? Probably not, as it's not working. I would appreciate any help on how to achieve this. Also, I've not given it too much thought, but is this a preferred way to handle such exceptions?
Assuming that your myApp.Context is an Activity and it has no back stack, you can just call Finish()
var context = myApp.Context; // this needs to be an Activity-based context...
context.RunOnUiThread(() =>
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Request failed. No connection.")
.SetPositiveButton("OK", (senderAlert, args) =>
{
context.Finish();
})
.Create();
alertDialog.Show();
});
Are you reusing this exception in several places, or is this a one off?
If your only using this exception once, there is no real reason to build your own.
You may as well just capture the exception and post your alert from inside your catch.
I know that's not as pretty of a way to write the catch, but if it works why not use it.
Side note:
DisplayAlert may be easier for you as well. It'll be a one liner.
Example:
await DisplayAlert("Failure","Request failed. No connection.", "Ok");
The way you are handling possible errors contains multiple issues and is not the right approach for several reasons.
First: Your code doesn't follow C-Sharp conventions and contains several code-smells. I show you a better and more accepted style.
1) Methods in C# normally starts with an uppercase letter. doLogin becomes Login
2) To create a new Uri instance you do not need to format your url-string. The string.Empty won't be used. So the code can be simplified into await GetAsync(new Uri(...));
3) The return_string seems not to be used in any way outside the method. It is string.Empty or "success". Why not switch it to bool? That way you can easily check if the login was successful. The return-type becomes bool instead of string.
The method looks now like this:
public async Task<bool> Login(string username, string password)
{
//TODO: Do parameter check for username and password
try
{
var response = await GetAsync(new Uri(Constants.loginEndpoint + username + "/" + password + "/"));
if (response.IsSuccessStatusCode)
{
// Process the positive response here
return true;
}
else
{
return false;
}
}
catch (Exception ex)
{
throw new ConnectionException();
}
return false;
}
Second, as mentioned by #Jason, an exception should not contain any UI or business logic. Consider the following, which will break your current implementation.
public async Task<bool> Login(string username, string password)
{
var connectionEx = new ConnectionException();
try
{
...
}
catch (Exception ex)
{
throw connectionEx;
}
...
}
Now your user will see the exception even so there wasn't any.
The last thing is that I recommend not to catch the exception just to throw your custom exception. The reason is, that there might be other things that raise an exception too. For example something is null in the positive response handling.
Depending on how the Login method is used, for example directly in an Android Activity, I would do something like that:
public async Task Login(string username, string password)
{
//TODO: Do parameter check for username and password
try
{
var response = await GetAsync(new Uri(Constants.loginEndpoint + username + "/" + password + "/"));
if (response.IsSuccessStatusCode)
{
// Process the positive response here
}
else
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Request failed.")
.SetPositiveButton("OK", (senderAlert, args) =>
{
Finish();
})
.Create();
alertDialog.Show();
}
}
catch (Exception ex)
{
var alertDialog = new AlertDialog.Builder(context)
.SetTitle("Failure")
.SetMessage("Something went wrong (" + ex.Message +")")
.SetPositiveButton("OK", (senderAlert, args) =>
{
Finish();
})
.Create();
alertDialog.Show();
}
}

C# await on GUI thread and catch exceptions WPF

I am using MVVM Light and there is a interface IDialogService which for used for showing dialogs. This interface has been implemented in App.xaml.cs
One specific method is interesting:
Task<bool> ShowMessage(string message, string title, string buttonConfirmText, string buttonCancelText, Action<bool> afterHideCallback);
The method is implemented as:
public Task<bool> ShowMessage(string message, string title, string buttonConfirmText,
string buttonCancelText,
Action<bool> afterHideCallback)
{
return Task.Factory.StartNew(() =>
{
var style = new Style(typeof(MessageBox));
style.Setters.Add(new Setter(MessageBox.OkButtonContentProperty, buttonConfirmText));
style.Setters.Add(new Setter(MessageBox.CancelButtonContentProperty, buttonCancelText));
var result = MessageBox.Show(_GetActiveWindow(), message, title,
MessageBoxButton.OKCancel,
MessageBoxImage.Question, style) == MessageBoxResult.OK;
if (afterHideCallback != null) afterHideCallback(result);
return result;
Where _currentTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); is defined in OnStartup
So usually we should call this method with await in front to get boolean value:
var result = await DialogService.ShowMessage(
Resources.Areyousure,Resources.Warning,
Resources.Yes, Resources.No, null);
So far so good. Now I have a wrapper method to execute code and catch exceptions and then show messaged box with error.
public bool TryCatchExecution(Action action, string successMessage = null)
{
try
{
action();
if (!string.IsNullOrEmpty(successMessage))
DialogService.ShowMessage(successMessage, Resources.Success);
return true;
}
catch (LogException ex)
{
DialogService.ShowError(ex.Error.LogMessage, Resources.Error, Resources.OK, null);
}
catch (Exception ex)
{
DialogService.ShowError(ex.Message, Resources.Error, Resources.OK, null);
}
return false;
}
And now I have a problem. If I use like sample A the GUI thread is blocked at the line var result = DialogService.ShowMessage . But if I use as in sample B the GUI thread in not blocked, message box shown and everything works like it should. Until I get an exception. The exception is unhanded by code. The error is "A first chance exception of type 'System.ServiceModel.FaultException`1' occurred in mscorlib.dll" and application crashes. As I have been reading this has something to do with SynchronizationContext.
//Sample A
private void ExecuteDeleteCommand()
{
TryCatchExecution(() =>
{
var result = DialogService.ShowMessage(
Resources.Areyousure,
Resources.Warning,
Resources.Yes,
Resources.No, null).Result;
if (!result) return;
_datalayer.DeleteField(FieldSelected);
Refresh();
FieldEdit = new MsgSqlFieldMapping();
RaisePropertyChanged("SqlRepository");
DialogService.ShowMessage(Resources.OperationSucceeded, Resources.Success);
});
}
//Sample B
private void ExecuteDeleteCommand()
{
TryCatchExecution(async () =>
{
var result =await DialogService.ShowMessage(
Resources.Areyousure,
Resources.Warning,
Resources.Yes,
Resources.No, null);
if (!result) return;
_datalayer.DeleteField(FieldSelected);
Refresh();
FieldEdit = new MsgSqlFieldMapping();
RaisePropertyChanged("SqlRepository");
await DialogService.ShowMessage(Resources.OperationSucceeded, Resources.Success);
});
}
Please help me to understand whats happening here and how to deal with it.
THNX a lot.
Your problem is due to async void - specifically, by passing an async lambda as an argument of type Action, you're creating an async void method. One of the problems with async void methods is that you can't catch exceptions (at least, not the normal way).
To resolve this, create an overload of your helper method that takes the async equivalent of Action, which is Func<Task>:
public async Task<bool> TryCatchExecution(Func<Task> action, string successMessage = null)
{
try
{
await action();
if (!string.IsNullOrEmpty(successMessage))
DialogService.ShowMessage(successMessage, Resources.Success);
return true;
}
catch (LogException ex)
{
DialogService.ShowError(ex.Error.LogMessage, Resources.Error, Resources.OK, null);
}
catch (Exception ex)
{
DialogService.ShowError(ex.Message, Resources.Error, Resources.OK, null);
}
return false;
}

Correct pattern to call a service containing an async call from an MVC controller

I am new to TAP and async/await in practice in C# so I may have some bad code smells here, so be gentle. :-)
I have a service method that looks as follows:
public OzCpAddOrUpdateEmailAddressToListOutput AddOrUpdateEmailAddressToList(
OzCpAddOrUpdateEmailAddressToListInput aParams)
{
var result = new OzCpAddOrUpdateEmailAddressToListOutput();
try
{
var mailChimManager = new MailChimpManager(aParams.MailChimpApiKey);
Task<Member> mailChimpResult =
mailChimManager.Members.AddOrUpdateAsync(
aParams.Listid,
new Member
{
EmailAddress = aParams.EmailAddress
});
//Poll async task until it completes.
//Give it at most 8 seconds to do what it needs to do
var outOfTime = DateTime.Now.AddSeconds(8);
while (!mailChimpResult.IsCompleted)
{
if (DateTime.Now > outOfTime)
{
throw new Exception("Timed out waiting for MailChimp API.");
}
}
//Should there have been a problem with the call then we raise an exception
if (mailChimpResult.IsFaulted)
{
throw new Exception(
mailChimpResult.Exception?.Message ??
"Unknown mail chimp library error.",
mailChimpResult.Exception);
}
else
{
//Call to api returned without failing but unless we have
//the email address subscribed we have an issue
if (mailChimpResult.Result.Status != Status.Subscribed)
{
throw new Exception(
$"There was a problem subscribing the email address
{aParams.EmailAddress} to the mailchimp list id
{aParams.Listid}");
}
}
}
catch (Exception ex)
{
result.ResultErrors.AddFatalError(PlatformErrors.UNKNOWN, ex.Message);
}
return result;
}
But when I call in from MVC Controller action mailChimpResult.IsCompleted always returns false and eventually I hit the timeout.
I realise this is because I am not chaining the async calls as per HttpClient IsComplete always return false and because of different threads this behaviour is "expected".
However I want my service method to hide the complexity of the async nature of what it is doing and merely do what appears to be a synchronous call in my action method namely:
var mailChimpResult =
_PlatformMailChimpService.AddOrUpdateEmailAddressToList(
new OzCpAddOrUpdateEmailAddressToListInput
{
EmailAddress = aFormCollection["aEmailAddress"],
Listid = ApplicationSettings.Newsletter.MailChimpListId.Value,
MailChimpApiKey = ApplicationSettings.Newsletter.MailChimpApiKey.Value
});
if (mailChimpResult.Result == true)
{
//So something
}
Ideally you should avoid the .Result and .IsFaulted properties of the Task and Task<T> objects, that was code smell number one. When you're using these objects you should use async and await through the entire stack. Consider your service written this way instead:
public async Task<OzCpAddOrUpdateEmailAddressToListOutput>
AddOrUpdateEmailAddressToList(
OzCpAddOrUpdateEmailAddressToListInput aParams)
{
var result = new OzCpAddOrUpdateEmailAddressToListOutput();
try
{
var mailChimManager = new MailChimpManager(aParams.MailChimpApiKey);
Member mailChimpResult =
await mailChimManager.Members.AddOrUpdateAsync(
aParams.Listid,
new Member
{
EmailAddress = aParams.EmailAddress
});
}
catch (Exception ex)
{
result.ResultErrors.AddFatalError(PlatformErrors.UNKNOWN, ex.Message);
}
return result;
}
Notice that I was able to remove all of that unnecessary polling and examining of properties. We mark the method as Task<OzCpAddOrUpdateEmailAddressToListOutput> returning and decorate it with the async keyword. This allows us to use the await keyword in the method body. We await the .AddOrUpdateAsync which yields the Member.
The consuming call into the service looks similar, following the same paradigm of async and await keywords with Task or Task<T> return types:
var mailChimpResult =
await _PlatformMailChimpService.AddOrUpdateEmailAddressToList(
new OzCpAddOrUpdateEmailAddressToListInput
{
EmailAddress = aFormCollection["aEmailAddress"],
Listid = ApplicationSettings.Newsletter.MailChimpListId.Value,
MailChimpApiKey = ApplicationSettings.Newsletter.MailChimpApiKey.Value
});
if (mailChimpResult.Result == true)
{
//So something
}
It is considered best practice to postfix the "Async" word to the method, to signify that it is asynchronous, i.e.; AddOrUpdateEmailAddressToListAsync.

Catching exception (and ignoring) in called method, but need to throw it in caller

I have a method that tries to get a web page. I want to attempt to get it several times so I built a wrapper around it to retry several times. In the method called I catch and then ignore the exception returning null. Therefore, after the first attempt the retries will occur. Here is the called method:
internal static async Task<string> WebClientAsync(string URI, NetworkCredential Creds = null, Dictionary.FantasySite Site = Dictionary.FantasySite.Other)
{
if (Creds == null)
{
try
{ //attempt to get the web page
HttpClient client = new HttpClient(); //create client
HttpResponseMessage response = await client.GetAsync(URI); //get response
response.EnsureSuccessStatusCode(); //ensure the response is good (or throw Exception)
return await response.Content.ReadAsStringAsync(); //return the string back
}
catch (HttpRequestException)
{
//MessageBox.Show(string.Format("\nHttpRequestException Caught!\nMessage :{0} for URI {1}.", e.Message, URI));
return null; //Catch the exception because we wrapped this and are trying again (null is the indicator it didn't work)
}
catch (Exception)
{
//MessageBox.Show(string.Format("\nException Caught!\nMessage :{0} for URI {1}.", e.Message, URI)); //TODO - THis hasn't happened, but remove it for production
return null; //Catch the exception because we wrapped this and are trying again (null is the indicator it didn't work)
}
}
}
If this still fails after all the retries then I want to throw the exception, but since I threw it away, I can't. Here is the calling method.
internal static async Task<string> WebClientRetryAsync(string URI, NetworkCredential Creds = null, Dictionary.FantasySite Site = Dictionary.FantasySite.Other)
{
string webPage = null;
for (int i = 0; i < Dictionary.WEB_PAGE_ATTEMPTS; i++) //Attempt to get the webpage x times
{
System.Diagnostics.Debug.Print(string.Format("WebClientRetryAsync attempt {0} for {1}", i + 1, URI));
//wait some time before retrying just in case we are too busy
//Start wait at 0 for first time and multiply with each successive failure to slow down process by multiplying by i squared
Thread.Sleep(Wait.Next(i * i * Dictionary.RETRY_WAIT_MS));
webPage = await WebClientAsync(URI, Creds, Site);
if (webPage != null) { break; } //don't attempt again if success
}
/*TODO - If webPage is null we didn't have success and need to throw an exception.
* This is done in the calls to this method and should be done here, move code over */
return webPage;
}
Can someone suggest if this is a bad approach and how I could refactor the code to throw the exception after failing too many times? Should I pass the exception to the calling method and ignore it until the retries have run out?
Yup. You should not throw away exceptions that you wish to rethrow. One possible approach is the following (trying to make a minimal amount of modifications to your current code):
internal static async Task<string> WebClientAsync(string URI, NetworkCredential Creds = null, Dictionary.FantasySite Site = Dictionary.FantasySite.Other)
{
// If (Creds == null) removed, you must return a task or throw an exception.
//attempt to get the web page
HttpClient client = new HttpClient(); //create client
HttpResponseMessage response = await client.GetAsync(URI); //get response
response.EnsureSuccessStatusCode(); //ensure the response is good (or throw Exception)
return await response.Content.ReadAsStringAsync(); //return the string back
}
internal static async Task<string> WebClientRetryAsync(string URI, NetworkCredential Creds = null, Dictionary.FantasySite Site = Dictionary.FantasySite.Other)
{
// assumes you have .NET 4.5, otherwise save exception.
// uses System.Runtime.ExceptionServices;
ExceptionDispatchInfo exceptionDispatchInfo = null;
for (int i = 0; i < Dictionary.WEB_PAGE_ATTEMPTS; i++) //Attempt to get the webpage x times
{
System.Diagnostics.Debug.Print(string.Format("WebClientRetryAsync attempt {0} for {1}", i + 1, URI));
try
{
var webPage = await WebClientAsync(URI, Creds, Site);
return webPage;
}
catch (Exception ex)
{
// save exception so it can be rethrown.
exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex);
}
// Edit: also need to do an async wait (no thread.sleep):
if (i < Dictionary.WEB_PAGE_ATTEMPTS - 1)
{
//wait some time before retrying just in case we are too busy
//Start wait at 0 for first time and multiply with each successive failure to slow down process by multiplying by i squared
await Task.Delay(Wait.Next(i * i * Dictionary.RETRY_WAIT_MS));
}
}
Debug.Assert(exceptionDispatchInfo != null); // shouldn't be null if we get here.
exceptionDispatchInfo.Throw();
}

Categories

Resources