Hoping that someone will be able to point me in the right direction. I have a console application that uses async method calls and I'm having a bit of trouble with the try/catch error blocks. Basically, when a catch is triggered it executes the logging code within the catch but it still throws the error up to the top level and terminates the overall execution of the app.
I've been looking through other queries about this and from what I've seen this type of behaviour can be caused by either an async void or an async Task with no await. Looking at my code I don't think I have either so I'm a bit stumped.
The error being introduced is a missing connection string (in the context file, not shown), which should throw a handled exception in the PayloadRepo method. Stepping through the debugger this error is indeed caught and logged as expected, however it still seems to bubble back up to the Main method and cause the overall execution to break.
Can anyone spot what may be happening?
Thank you!
static async Task Main(string[] args)
{
BuildLogConfiguration();
BuildDependencyContainer();
await RunProgram();
}
static async Task RunProgram()
{
systemLog.Info("Started full process at: " + System.DateTime.Now);
using (var scope = Container.BeginLifetimeScope())
{
var payloadService = scope.Resolve<IPayloadService>();
await payloadService.ProcessPayloadData(scope);
}
systemLog.Info("Completing full process at: " + System.DateTime.Now);
}
public class PayloadService : IPayloadService
{
private static readonly ILog _systemLog = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public async Task ProcessPayloadData(ILifetimeScope scope)
{
await PayloadData_Process(scope);
}
private async Task PayloadData_Process(ILifetimeScope scope)
{
var repo = scope.Resolve<IPayloadRepo>();
var payloadList = await repo.Payload_Get();
}
}
public class PayloadRepo : IPayloadRepo
{
public async Task<IEnumerable<Payload>> Payload_Get()
{
using (var context = new ASGB_DataLayerContext())
{
try
{
var payloadList = context.Payloads;
var result = await payloadList.ToListAsync();
return result;
}
catch (Exception ex) <<--- CATCHES THIS AND EXECUTES CODE WITHIN, BUT IT STILL THROWS AN ERROR ON RunProgram().Wait() AND TERMINATES PROGRAM
{
systemLog.Error(ex.Message);
systemLog.Error(ex.InnerException);
var entries = context.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged);
foreach (var entry in entries)
{
foreach (var prop in entry.CurrentValues.Properties)
{
var val = prop.PropertyInfo.GetValue(entry.Entity);
systemLog.Error($"{prop.ToString()} ~ ({val?.ToString().Length})({val})");
}
}
systemLog.Error("------------------------------------------------------------------------");
return null;
}
}
}
}
Related
There are some tasks that I attached an action to. I have a middleware for observing all of the exceptions. Now, there is a problem with thread exception. I've already thrown an exception manually in body of the action, but when I throw an exception the application goes to break mode state and I can't monitor the exceptions.
Before mvc in configure, I put my error handling middleware
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context /* other dependencies */)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
var code = HttpStatusCode.InternalServerError;
var result = JsonConvert.SerializeObject(new { error = exception.Message });
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
//and this is the piece of code that run all tasks.
foreach (var item in BotList)
{
BotHandler BotObject = new BotHandler(item,conn);
Task.Run(() => { BotObject.Run();});
}
//
public void Run()
{
//BotClient.StopReceiving();
BotClient.OnMessage += BotOnMessageReceived;
BotClient.OnMessageEdited += BotOnMessageReceived;
BotClient.OnCallbackQuery += BotOnCallbackQueryReceived;
}
private async void BotOnMessageReceived(object sender, MessageEventArgs messageEventArgs)
{
try
{
//do something
string a = null;
var b = a.ToString();
}
catch(Exception exp )
{
throw exp
}
}
}
As I understood you run this code in action of controller:
//and this is the piece of code that run all tasks.
foreach (var item in BotList)
{
BotHandler BotObject = new BotHandler(item,conn);
Task.Run(() => { BotObject.Run();});
}
The main problem is that you are trying to run the task for an already finished request. That is why ExceptionHandlingMiddleware (and actually other middlewares) can't handle anything. To fix your issue you can add try/catch block to handle an unexpected exception.
I would strongly advise not to start a background task during an HTTP request. It's error-prone approach as a task may shut down at any time and you won't even notice. Instead of this approach, it's better to use background task (msdn, SO Discussion), some kind of AWS lambda/Azure function or another task scheduler.
In case you need to do some recalculation job after HTTP method is called you may consider async message processing to trigger the process.
if you go here https://msdn.microsoft.com/en-us/magazine/jj991977.aspx you will read that.
Exceptions from an Async Void Method Can’t Be Caught with Catch
So you cannot catch exception (the middleware cannot) which you are throwing in your BotOnMessageReceived method (in a Catch part).
So you have 2 solutions.
Remove async keyword
Or Catch app unhandled exceptions if its possible. For example in normal .net framework console app, you have event in App called unhandled exceptions, and can handle such situations like yours.
I have also found smth here, maybe it will help
How do I catch unhandled exceptions in ASP .NET Core 2.0 before the page is rendered?
I have a function (below) that I retrieve data from an API. If I set a breakpoint at the line that deserializes it, then I can see that it is populated with data which is great.
When I continue on, it goes into the second function (below) and it throws an error. The error says next to it Not yet computed, and therefore throwing an exception.
When I do it with a small list it works just fine (I presume its cos it's a small set of data).
How is this possible when I'm using ContinueWith (waiting for the task to complete)?
public static async Task<Data> GetAllCardsInSet(string setName)
{
setName = WebUtility.UrlEncode(setName);
var correctUri = Path.Combine(ApiConstants.YugiohGetAllCardsInSet, setName);
Console.WriteLine();
using (var httpClient = new HttpClient())
{
var response =
await httpClient.GetAsync(correctUri);
var result = await response.Content.ReadAsStringAsync();
var cardData = JsonConvert.DeserializeObject<CardSetCards>(result);
for (int i = 0; i < cardData.Data.Cards.Count; i++)
{
cardData.Data.Cards[i] = FormatWords(cardData.Data.Cards[i]);
}
return cardData.Data;
}
}
private void GetYugiohCardsAndNavigate(string name)
{
var cardSetData = YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name).ContinueWith((result) =>
{
//var cards = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
try
{
this.mainPage.NavigateToYugiohCardListPage(result.Result);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App", "Sorry, we could not fetch this set");
}
});
}
Your GetAllCardsInSet method no need to change.
But using of this method can be refactored.
Method GetAllCardsInSet return Task and you not observed the completion of the this Task.
You need to check is Task completes succesfully, easiest approach to use await keyword. Awaiting task will unwrapp returned value or throw exception if task completed with exception.
For using async/await in the GetYugiohCardsAndNavigate change method signature to aynchronous and returning Task
private async Task GetYugiohCardsAndNavigate(string name)
{
try
{
var cardSetData = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
this.mainPage.NavigateToYugiohCardListPage(cardSetData);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App",
"Sorry, we could not fetch this set");
}
}
you called an async method in a sync method without Wait. It should have been done like:
YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name).ContinueWith((result) =>
{
//var cards = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
try
{
this.mainPage.NavigateToYugiohCardListPage(result.Result);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App", "Sorry, we could not fetch this set");
}
}).Wait();
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 am struggling around the (seems so) pretty famous problem of the exception handling by using the async/await pattern. Specifically my context is on a HTTP client, but I have also tried with a much simpler test, and it behaves the same.
Consider the below program, which is a super-simplified version of my original app's context.
class Program
{
static void Main(string[] args)
{
Test();
Console.Write("Press any key...");
Console.ReadKey();
Console.WriteLine();
}
static async void Test()
{
var c = new MyClient();
try
{
var uri = new Uri("http://www.google.com/"); //valid address
var s = await c.GetString(uri);
Console.WriteLine(s.Length);
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
}
try
{
var uri = new Uri("http://www.foo.bah/"); //non-existent address
var s = await c.GetString(uri);
Console.WriteLine(s.Length);
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
}
}
}
class MyClient
{
public async Task<string> GetString(Uri uri)
{
var client = new HttpClient();
return await client.GetStringAsync(uri);
}
}
When the program starts, it downloads the first web site's page as a string, then displays its length: that's fine. Afterward, when the same operation is performed against an invalid address, the client raises a WebException (that's what I want), but it's not caught.
UPDATE: as "not caught", I mean that the code actually does not flow through the "catch" branch and silently displays the exception message. Instead, the exception is shown by the VS IDE, and the debugging breaks.
Any decent solution to catch the exception?
Many thanks in advance.
Although you have already figured out the exception is HttpRequestException not WebException, still I would like to highlight few important things about async-await operator usage.
async void is of type fire & forget and is only & only for event handlers.
As soon as compiler reaches first await operator inside async method control returns to the caller.
Debugging your code :-
Since you are using async void in Test method so the control returns to the caller and execution continues to line Console.Write("Press any key..."); without having any information about the Task and then you are waiting for the user input.
In the meanwhile response from awaited method comes and the execution continues inside Test method.
If you comment out the line Console.ReadKey(); inside main() OR user provides input immediately then you'll notice that response may or may not get printed. This is because you are not waiting on the Task getting executed you simply trusted on the user that he will not enter anything till your Task completes.
Solution:-
Solution is to return Task from Test() and then wait till it finishes, below is the updated code also note adding Async at the end of method name is the naming convention you must follow to save you from the headache of distinguishing between asynchronous and synchronous methods.
class Program
{
static void Main(string[] args)
{
Task task = TestAsync();
Console.Write("Press any key...");
task.wait();
//Console.ReadKey();
Console.WriteLine();
}
static async Task<string> TestAsync()
{
var c = new MyClient();
try
{
var uri = new Uri("http://www.google.com/"); //valid address
var s = await c.GetStringAsync(uri);
Console.WriteLine(s.Length);
}
catch (HttpRequestException ex)
{
Console.WriteLine(ex.Message);
}
try
{
var uri = new Uri("http://www.foo.bah/"); //non-existent address
var s = await c.GetStringAsync(uri);
Console.WriteLine(s.Length);
}
catch (HttpRequestException ex)
{
Console.WriteLine(ex.Message);
}
//to avoid compiler error
return null;
}
}
class MyClient
{
public async Task<string> GetStringAsync(Uri uri)
{
var client = new HttpClient();
return await client.GetStringAsync(uri);
}
}
What is the proper way to show a message dialog due to a caught exception?
I originally tried
try
{
await DoSomething();
}
catch(InvalidOperation ex)
{
await MessageDialog(ex.Message).ShowAsync();
}
catch(CommunicationException)
{
await MessageDialog(StringResourceLoader.LoginError).ShowAsync();
}
This did not work because you cannot await inside of a try block. Taking the await commands out makes the compiler shows the following warning:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call
I don't like keeping those warnings in my code, because in several spots people have forgotten to use await and thus had hard to find bugs.
Changing the message dialog statement to var task = new MessageDialog(ex.Message).ShowAsync().AsTask(); gets rid of all warnings and errors, but I am not sure that is a good way to go about it (and technically is bad for the same reason it wants me to await the call)
Finally, I tried storing the exception and doing my logic of what to display to the user (including all logic on determining what type of exception was thrown) outside of the catch, via:
Exception thrownException = null;
try
{
await DoSomething();
}
catch(Exception ex)
{
thrownException = ex;
}
if (thrownException is InvalidOperationException)
await MessageDialog(ex.Message).ShowAsync();
else if (thrownException is CommunicationException)
await MessageDialog(StringResourceLoader.LoginError).ShowAsync();
I am not sure I feel that this is the best way to go about it either. Any ideas how this should be done?
Edit:
After Hans' comment about other issues, I ended up solving it by creating the following class:
public static class MessageDialogDisplayer
{
private static readonly ConcurrentQueue<MessageDialog> _dialogQueue;
private static readonly CancellationTokenSource _cancellationTokenSource;
static MessageDialogDisplayer()
{
_dialogQueue = new ConcurrentQueue<MessageDialog>();
_cancellationTokenSource = new CancellationTokenSource();
new Task(DisplayQueuedDialogs, _cancellationTokenSource.Token).Start();
}
public static void DisplayDialog(MessageDialog dialog)
{
_dialogQueue.Enqueue(dialog);
}
private static async void DisplayQueuedDialogs()
{
const int millisecondsBetweenDialogChecks = 500;
while (!_cancellationTokenSource.Token.IsCancellationRequested)
{
MessageDialog dialogToDisplay;
if (_dialogQueue.TryDequeue(out dialogToDisplay))
{
await dialogToDisplay.ShowAsync();
}
else
{
await Task.Delay(millisecondsBetweenDialogChecks);
}
}
}
}
This has changed my try/catch statement to
MessageDialog errorDialog = null;
try
{
await DoSomething();
}
catch(InvalidOperation ex)
{
MessageDialogDisplayer.DisplayDialog(new MessageDialog(ex.Message));
}
catch(CommunicationException)
{
MessageDialogDisplayer.DisplayDialog(new MessageDialog(StringResourceLoader.LoginError));
}
Which makes things more stable in the long run (once I convert all message dialog callers to use this instead of showAsyncing themselves) and makes the try/catch block a lot less messy (imo).