I have an implementation like this:
Task<IEnumerable<Item1>> GetItems1()
{
return RunRequest(async () => ParseItemsFromResponse(await(httpClient.Get(..))));
}
Task<IEnumerable<Item2>> GetItems2()
{
return RunRequest(async () => ParseItemsFromResponse(await httpClient.Get(..)));
}
TResult RunRequest<TResult>(Func<TResult> req)
{
try
{
return req();
}
catch (Exception ex)
{
// Parse exception here and throw custom exceptions
}
}
The issue is the void anonymous method async () => ParseItemsFromResponse(..)
.
Since it returns void and not a Task
, if there's an exception thrown within the anonymous method, it's actually not going to be caught by the try
and catch
within the RunRequest
.
Any suggestions how to refactor this?
RunRequest
should take a Func<Task<TResult>>
, as such:
async Task<TResult> RunRequestAsync<TResult>(Func<Task<TResult>> req)
{
try
{
return await req().ConfigureAwait(false);
}
catch (Exception ex)
{
// Parse exception here and throw custom exceptions
}
}
Then your async
lambdas are converted to async Task<T>
methods instead of async void
.
I have more information on sync/async delegates on my blog.
ConfigureAwait(false)
? Or is it just an optimization not to switch context back? - Don Box
async void
. They are only meant for event handlers or similar methods that can't receive or handle aTask
result. You can't an exception thrown byasync void
except by the application's Unhandled Exception handler - Panagiotis KanavosRunRequest
take aTask
instead ofFunc
and pass in thehttpClient.Get()
directly? - Mats391RunRequest
overload unless ParseItemsFromResponse returns something. Do you have another one forAction
? Just don't do it like this.var result=await httpClient.Get();ParseItems(result);
is cleaner - Panagiotis Kanavos