Exception handling with async delegate
I am trying to figure out what I am doing wrong here. I'm sure this is something silly, but I could use extra eyes to figure out what it is. I am trying to create a worker service that you can assign a worker action delegate to. When I try to catch the exception thrown by the worker activity delegate, the handler is not called. I think this is because my action is actually an asynchronous method that returns a Task, and it is not expected. But how am I supposed to know that the delegate is asynchronous? Why does C # allow me to assign a method that returns a task to an action variable that should return void?
Any help would be appreciated!
[Fact]
public async Task CanCatchExceptions() {
var worker = new Worker {
WorkAction = async item => await Throws()
};
worker.DoWork(new WorkItem { Id = 1 });
}
public class Worker {
// would prefer to keep work action simple and not require a Task.
public Action<WorkItem> WorkAction { get; set; }
public void DoWork(WorkItem item) {
try {
WorkAction(item);
} catch {
Debug.WriteLine("Handled");
}
}
}
public class WorkItem {
public int Id { get; set; }
}
public async Task Throws() {
throw new ApplicationException();
}
source to share
Why does C # allow me to assign a method that returns a task to an action variable that should return void?
Since it async void
is legal use case for async event handlers, why does the compiler allow void to return Action<T>
and doesn't complain. Once you know this fact, you can explicitly use Func<Task>
that will create the desired method return method Task
.
As you say, you prefer the work activity to be "simple" and does not require a task to complete, consider supplying an asynchronous overload that will behave correctly with an asynchronous control flow:
public Func<WorkItem, Task> WorkAsync { get; set; }
public async Task WorkAsync(WorkItem item)
{
try
{
await WorkAsync(item)
}
catch (Exception e)
{
// Handle
}
}
Note that this could be in a completely different class (separate from the async worker with sync) of your choice.
source to share
Lambdas with no return values ββcan be passed to a task return method (for example Func<Task>
) or a void return method (for example Action
). Note that when passing void to a return method, the actual method of this lambda is a method async void
with all the problems that come with methods async void
. I describe some of the issues async void
in my best practices article ; one of them is that you cannot catch exceptions with try
.
The best option is to modify your work items to return Task
, which is a much more natural representation of asynchronous methods.
source to share