Continue try block after catch. Log all exceptions in one try catch - c#

I want to do 4 actions and each one can throw an exception. I need to execute them all and log all errors that happened along the way
I can do it in a big and clumsy way :
var exceptions = new List<ExceptionType1>();
try {
result1 = await action1();
} catch (ExceptionType1 ex1) {
exceptions.Add(ex1);
logger.Log(ex1);
}
try {
result2 = await action2();
} catch (ExceptionType1 ex1) {
exceptions.Add(ex1);
logger.Log(ex1);
}
try {
result3 = await action3(result1);
} catch (ExceptionType2 ex2) {
var ex1 = new ExceptionType1(ex2);
exceptions.Add(ex1);
logger.Log(ex1);
}
try {
result4 = await action4(result2);
} catch (ExceptionType2 ex2) {
var ex1 = new ExceptionType1(ex2);
exceptions.Add(ex1);
logger.Log(ex1);
}
if (exceptions.Count > 0) {
return false;
}
Obviously sometimes things like that bloat in size quickly
I would like to do it in more elegant way :
var exceptions = new List<ExceptionType1>();
try {
result1 = await action1();
result2 = await action2();
result3 = await action3(result1);
result4 = await action4(result2);
} catch(Exception ex) {
var ex1 = new ExceptionType1(ex);
exceptions.Add(ex1);
logger.Log(ex1);
???
~~Goto try block and continue executing~~
???
}
if (exceptions.Count > 0) {
return false;
}
I tried to find info how to get back into try block and continue executing but was unsuccessfull. Is it possible. Is there any other way around the problem that I didn't consider?

One way I guess (given your reequipments) is to create a local method
var exceptions = new List<Exception>();
async Task<T> DoAsync<T>(Func<Task<T>> func)
{
try
{
return await func();
}
catch (Exception ex)
{
exceptions.Add(ex);
logger.Log(ex);
}
return default;
}
result1 = await DoAsync(action1);
result2 = await DoAsync(action2);
if (exceptions.Count > 0)
{
return false;
}

Maybe you can extract this into a local function:
// declare this inside the method that contains the code you showed
async void GetResult<TResult, TException>(out TResult result, Func<Task<TResul>> resultGetter, Func<TException, ExceptionType1> mapper)
where TException: Exception {
try {
result = await resultGetter();
} catch (TException ex) {
var ex1 = mapper(ex)
exceptions.Add(ex1);
logger.Log(ex1);
}
}
Callers:
GetResult(out result1, action1, (ExceptionType1 e) => e);
GetResult(out result2, action2, (ExceptionType1 e) => e);
GetResult(out result3, async () => await action3(result1), (ExceptionType2 e) => new ExceptionType1(e));
GetResult(out result4, async () => await action4(result2), (ExceptionType2 e) => new ExceptionType1(e));

how to get back into try block and continue executing.. Is it possible?
You can't do what you are looking for: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-catch
The block is executed until an exception is thrown or it is completed successfully.
Once out of the block, even the variables that have been initialized are rolled back.
It's the whole point of the exception to stop the execution of the safe guarded code.
Follow the recommendations of the other two answers to simplify your code

I think WhenAll() method for Task will suit your purpose:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-catch#taskwhenall-example
First of all, your actions are already async. Moreover, you can divide to several WhenAll() blocks if you need to follow any order of execution
Example from docs:
public async Task action1()
{
throw new InvalidOperationException();
}
public async Task action2()
{
throw new NotImplementedException();
}
public async Task action3()
{
throw new InvalidCastException();
}
public async Task DoMultipleAsync()
{
Task theTask1 = action1();
Task theTask2 = action2();
Task theTask3 = action3();
Task allTasks = Task.WhenAll(theTask1, theTask2, theTask3);
try
{
await allTasks;
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
Console.WriteLine("Task IsFaulted: " + allTasks.IsFaulted);
foreach (var inEx in allTasks.Exception.InnerExceptions)
{
Console.WriteLine("Task Inner Exception: " + inEx.Message);
}
}
}
// Output:
// Exception: Operation is not valid due to the current state of the object.
// Task IsFaulted: True
// Task Inner Exception: Operation is not valid due to the current state of the object.
// Task Inner Exception: The method or operation is not implemented.
// Task Inner Exception: Specified cast is not valid.

Related

Second operation started on this context before a previous asynchronous operation completed

public async Task<JobViewModel> Handle(AddJobCommand command, CancellationToken cancellationToken)
{
if (command.JobViewModel == null) throw new InvalidOperationException("Empty request.");
var jobViewModel = command.JobViewModel;
try
{
var job = _mapper.Map<DataAccess.Domain.Lab.Job>(jobViewModel);
_context.Set<DataAccess.Domain.Lab.Job>().Add(job);
if (job.Notes!= null)
{
var newNote = job.Notes.FirstOrDefault(n => n.IsNew);
if (newNote != null)
{
newNote.JobId = job.Id;
_context.Set<DataAccess.Domain.Lab.JobNote>().Attach(newNote);
_context.Entry(newNote).State = EntityState.Added;
}
}
await OnBeforeAdd(job); // job.Name = await GenerateJobName();
await _context.SaveChangesAsync();
jobViewModel.Id = job.Id;
return jobViewModel;
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
eve.Entry.Entity.GetType().Name, eve.Entry.State);
foreach (var ve in eve.ValidationErrors)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage);
}
}
throw;
}
catch (DbUpdateException e)
{
Console.WriteLine(e.Message);
throw;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
throw;
}
}
protected async Task OnBeforeAdd(DataAccess.Domain.Lab.Job job)
{
if (string.IsNullOrWhiteSpace(job.Name))
{
job.Name = await GenerateJobName();
}
}
private async Task<string> GenerateJobName()
{
Func<int, int, string> composeName = (year, number) => string.Format("S{0:D2}{1:D3}", year, number);
int currentYear = DateTime.UtcNow.Year % 100;
int newJobNumber = 501;
//if (_context.Jobs!= null)
// {
// string maxJobNumber = await _context.Jobs.MaxAsync(j => j.Name);
string maxJobNumber = await _context.Jobs.MaxAsync(j=>j.Name);
if (!string.IsNullOrWhiteSpace(maxJobNumber))
{
var matches = Regex.Matches(maxJobNumber, "S" + currentYear + "(?<num>[0-9]{3})");
if (matches.Count == 1)
{
newJobNumber = int.Parse(matches[0].Groups["num"].Value) + 1;
}
}
// }
string newName = composeName(currentYear, newJobNumber);
while (await _context.Jobs.AnyAsync(j => j.Name == newName))
{
newJobNumber++;
newName = composeName(currentYear, newJobNumber);
}
return newName;
}
}
}
Below line give error:
string maxJobNumber = await _context.Jobs.MaxAsync(j=>j.Name);
On running the code it throws below exception "A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe."
Also the same line "string maxJobNumber = await _context.Jobs.MaxAsync(j=>j.Name);" shows warning message as
converting null literal or possible null value to non-nullable type string error . Can this be an issue?
Some comments about the above code:
There's no need for these lines:
if (job.Notes!= null)
{
var newNote = job.Notes.FirstOrDefault(n => n.IsNew);
if (newNote != null)
{
newNote.JobId = job.Id;
_context.Set<DataAccess.Domain.Lab.JobNote>().Attach(newNote);
_context.Entry(newNote).State = EntityState.Added;
}
}
Once you've added the job to the DbSet, EF will automatically figure out that its JobNotes need to be added as well. And, it will determine there state appropriately.
This is the pattern I would use:
public async Task<JobViewModel> Handle(AddJobCommand command, CancellationToken cancellationToken)
{
var jobViewModel = command.JobViewModel;
var job = _mapper.Map<DataAccess.Domain.Lab.Job>(jobViewModel);
job.Name = await GenerateJobName(job.Name);
await _context.Set<DataAccess.Domain.Lab.Job>().AddAsync(job);
await _context.SaveChangesAsync();
jobViewModel.Id = job.Id;
return jobViewModel;
}
private async Task<string> GenerateJobName(DataAccess.Domain.Lab.Job job)
{
if (!string.IsNullOrWhiteSpace(job.Name)) return job.Name;
...
}
You may be overthinking all the try-catch stuff. It is more than half the code and is obscuring the real logic. I would move that out to a higher location and concentrate on making this do the one thing it's intended to do.
The reason the code performs differently in PROD is due to a race condition. There's no debugger in PROD, so your first calls are finishing with enough time to start the second calls.

Why can't I detect this Exception in my Tests

I am writing an Intigration test using nUnit in C#.
My code is:
public async Task<bool> WriteData(Document data, string tableName)
{
try
{
var table = Table.LoadTable(_dynamoDbClient, tableName);
table.PutItem(data);
return true;
}
catch (Exception e)
{
_logger.Info("Failed Writing Record");
_logger.Info("Error: " + e.Message);
return false;
}
}
My Test is:
public void TestToSeeIfWeGetAnExceptionWhenProvidingBadDataToTheDatabase()
{
// arrange
var item = new Document
{
["Id"] = "1001",
["TransactionID"] = 111111,
["StatementType"] = "TestBank"
};
_bankStatementTable = "does-not-exist";
// act / assert
Assert.Catch<System.Exception>(() => _awsDynamoDbManager.WriteData(item, _bankStatementTable));
}
Because I am passing bad data to the database I expect an exception and I get one.
However, the test fails.
I get this message from the test runner:
Expected: instance of <System.Exception>
But was: null
If I run the test in Debug I can see the Catch being hit.
What am I missing?
You should update your code a little bit. Rethrow an Exception in catch block and use ThrowsAsync
catch (Exception e)
{
_logger.Info("Failed Writing Record");
_logger.Info("Error: " + e.Message);
throw;
}
...
Assert.ThrowsAsync<System.Exception>(async () => await _awsDynamoDbManager.WriteData(item, _bankStatementTable));
Or check that WriteData returns false by making test as async as well and call Assert.IsFalse
public async Task TestToSeeIfWeGetAnExceptionWhenProvidingBadDataToTheDatabase()
{
// arrange
...
// act / assert
var result = await _awsDynamoDbManager.WriteData(item, _bankStatementTable);
Assert.IsFalse(result);
}
Since there is no awaitable code inside WriteData method, you can make it synchronous one and use Task.FromResult as return result or make it even simpler and remove using of Task<bool>
public bool WriteData(Document data, string tableName)
{
try
{
var table = Table.LoadTable(_dynamoDbClient, tableName);
table.PutItem(data);
return true;
}
catch (Exception e)
{
_logger.Info("Failed Writing Record");
_logger.Info("Error: " + e.Message);
return false; //or throw;
}
}
And the synchronous test code
Assert.Throws<System.Exception>(() => _awsDynamoDbManager.WriteData(item, _bankStatementTable));
or
public void TestToSeeIfWeGetAnExceptionWhenProvidingBadDataToTheDatabase()
{
// arrange
...
// act / assert
var result = _awsDynamoDbManager.WriteData(item, _bankStatementTable);
Assert.IsFalse(result);
}
Because you are returning false in case of Exception:
catch (Exception e)
{
_logger.Info("Failed Writing Record");
_logger.Info("Error: " + e.Message);
return false;
}
Use throw clause.

How to avoid dead lock in Task.WaitAll

I have multiple running tasks in Console application. In my task I call some method from EWS Managed API. So When EWS throw exception code execution is not return to WaitAll.
while (index < messageData.Count)
{
var mess = messageData[index];
var task = new System.Threading.Tasks.Task<EventsCreationResult>(() =>
{
EventsCreationResult taskResult = null;
taskResult = CreateEmail(mess, Service);
return taskResult;
});
task.Start();
tasks.Add(task);
index = index + 1;
}
System.Threading.Tasks.Task.WaitAll(tasks.ToArray());
And Function
public static EventsCreationResult CreateEmail(string folderName, EmailMessage mess)
{
EventsCreationResult result = new EventsCreationResult();
result.Key = mess.Subject;
try
{
mess.Save(FindFolder(folderName).Id);
result.EventsCreationLog = "Message created:" + mess.Subject;
}
catch (Exception ex)
{
result.Error = ex;
}
return result;
}
So how to avoid dead lock and if its possible do not cancel task which not rise error.

C# Cannot await in the body of a catch clause [duplicate]

I have the following code:
WebClient wc = new WebClient();
string result;
try
{
result = await wc.DownloadStringTaskAsync( new Uri( "http://badurl" ) );
}
catch
{
result = await wc.DownloadStringTaskAsync( new Uri( "http://fallbackurl" ) );
}
Basically I want to download from a URL and when it fails with an exception I want to download from another URL. Both time async of course. However the code does not compile, because of
error CS1985: Cannot await in the body of a catch clause
OK, it's forbidden for whatever reason but what's the correct code pattern here?
EDIT:
The good news is that C# 6.0 will likely allow await calls both in catch and finally blocks.
Update: C# 6.0 supports await in catch
Old Answer: You can rewrite that code to move the await from the catch block using a flag:
WebClient wc = new WebClient();
string result = null;
bool downloadSucceeded;
try
{
result = await wc.DownloadStringTaskAsync( new Uri( "http://badurl" ) );
downloadSucceeded = true;
}
catch
{
downloadSucceeded = false;
}
if (!downloadSucceeded)
result = await wc.DownloadStringTaskAsync( new Uri( "http://fallbackurl" ) );
Awaiting in a catch block is now possible as of the End User Preview of Roslyn as shown here (Listed under Await in catch/finally) and will be included in C# 6.
The example listed is
try … catch { await … } finally { await … }
Update: Added newer link, and that it will be in C# 6
This seems to work.
WebClient wc = new WebClient();
string result;
Task<string> downloadTask = wc.DownloadStringTaskAsync(new Uri("http://badurl"));
downloadTask = downloadTask.ContinueWith(
t => {
return wc.DownloadStringTaskAsync(new Uri("http://google.com/")).Result;
}, TaskContinuationOptions.OnlyOnFaulted);
result = await downloadTask;
Give this a try:
try
{
await AsyncFunction(...);
}
catch(Exception ex)
{
Utilities.LogExceptionToFile(ex).Wait();
//instead of "await Utilities.LogExceptionToFile(ex);"
}
(See the Wait() ending)
Use C# 6.0. see this Link
public async Task SubmitDataToServer()
{
try
{
// Submit Data
}
catch
{
await LogExceptionAsync();
}
finally
{
await CloseConnectionAsync();
}
}
You could put the await after the catch block followed by a label, and put a goto in the try block.
(No, really! Goto's aren't that bad!)
The pattern I use to rethrow the exception after await on a fallback task:
ExceptionDispatchInfo capturedException = null;
try
{
await SomeWork();
}
catch (Exception e)
{
capturedException = ExceptionDispatchInfo.Capture(e);
}
if (capturedException != null)
{
await FallbackWork();
capturedException.Throw();
}
You can use a lambda expression as follows:
try
{
//.....
}
catch (Exception ex)
{
Action<Exception> lambda;
lambda = async (x) =>
{
// await (...);
};
lambda(ex);
}
In a similar instance, I was unable to await in a catch block. However, I was able to set a flag, and use the flag in an if statement (Code below)
---------------------------------------...
boolean exceptionFlag = false;
try
{
do your thing
}
catch
{
exceptionFlag = true;
}
if(exceptionFlag == true){
do what you wanted to do in the catch block
}

A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property

These are my tasks. How should i modify them to prevent this error. I checked the other similar threads but i am using wait and continue with. So how come this error happening?
A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.
var CrawlPage = Task.Factory.StartNew(() =>
{
return crawlPage(srNewCrawledUrl, srNewCrawledPageId, srMainSiteId);
});
var GetLinks = CrawlPage.ContinueWith(resultTask =>
{
if (CrawlPage.Result == null)
{
return null;
}
else
{
return ReturnLinks(CrawlPage.Result, srNewCrawledUrl, srNewCrawledPageId, srMainSiteId);
}
});
var InsertMainLinks = GetLinks.ContinueWith(resultTask =>
{
if (GetLinks.Result == null)
{
}
else
{
instertLinksDatabase(srMainSiteURL, srMainSiteId, GetLinks.Result, srNewCrawledPageId, irCrawlDepth.ToString());
}
});
InsertMainLinks.Wait();
InsertMainLinks.Dispose();
You're not handling any exception.
Change this line:
InsertMainLinks.Wait();
TO:
try {
InsertMainLinks.Wait();
}
catch (AggregateException ae) {
/* Do what you will */
}
In general: to prevent the finalizer from re-throwing any unhandled exceptions originating in your worker thread, you can either:
Wait on the thread and catch System.AggregateException, or just read the exception property.
EG:
Task.Factory.StartNew((s) => {
throw new Exception("ooga booga");
}, TaskCreationOptions.None).ContinueWith((Task previous) => {
var e=previous.Exception;
// Do what you will with non-null exception
});
OR
Task.Factory.StartNew((s) => {
throw new Exception("ooga booga");
}, TaskCreationOptions.None).ContinueWith((Task previous) => {
try {
previous.Wait();
}
catch (System.AggregateException ae) {
// Do what you will
}
});

Categories

Resources