Async Lambda Issue - c#

Updated code (as per comment): still doesn't work
I am trying to create an extension method on Task to call my async database calls (fire and forget), however I am getting a compilation error:
Error CS4010
Cannot convert async lambda expression to delegate type 'Func<T>'. An async lambda expression may return void, Task or Task<T>, none of which are convertible to 'Func<T>'. AsyncTaskExtension
This is my code:
public static void ExecuteTask<T>(this Task<T> BackgroudJob, Action<T> UIJob, Action<Exception> OnError = null) where T : struct
{
Func<T> work = async () =>
{
T ret = default(T);
try
{
ret = await BackgroudJob;
UIJob(ret);
//throw new Exception("My exception !!!");
}
catch (Exception ex)
{
try
{
if (OnError != null)
{
OnError(ex);
}
System.Diagnostics.Debug.WriteLine(ex);
}
catch { }//eat exception
}
return ret;
};
work();
}

I am trying to create an extension method on Task to call my async database calls(fire and ferget)
OK, I have to say that this is a really, really bad idea!
"Fire and forget" means: "I don't care when this code completes. Or if it completes." Corollary: "I am perfectly OK with losing database writes during an appdomain recycle, process recycle, reboot, or pod replacement; and I am happy with not having any logs or other notifications about said data loss."
I cannot imagine how this would ever be acceptable for a database write operation. At most, fire-and-forget is sometimes used for things like updating caches. Some people think it's OK for emails, too, but I prefer a simple queue.
To make the method fire-and-forget, change the return type to void and the Func<T> to an Action:
public static void CreateFireAndForgetWork<T>(this Task<T> Job, Action<Exception> OnError = null) where T : struct
{
Action work = async () =>
{
try
{
await Job;
}
catch (Exception ex)
{
...
}
};
work();
}
This code does result in an async void lambda expression (since it is converted to an Action). Normally, this is a bad idea, but it's here because the code is doing fire-and-forget (which, as noted at the beginning of this post, is a bad idea IMO).

Ok finally it worked
public static void ExecuteTask<T>(this Task<T> BackgroudJob, Action<T> UIJob, Action<Exception> OnError = null) where T : struct
{
Action work = async () =>
{
T? ret = null;
try
{
ret = await BackgroudJob;
//throw new Exception("My exception !!!");
}
catch (Exception ex)
{
try
{
if (OnError != null)
{
OnError(ex);
}
System.Diagnostics.Debug.WriteLine(ex);
}
catch { }//eat exception
}
if (ret.HasValue)
{
UIJob(ret.Value);
}
};
work();
}

Related

How to handle errors in method that has IAsyncEnumerable as return type

I have an API endpoint:
[HttpGet("api/query")]
public async IAsyncEnumerable<dynamic> Query(string name)
{
await foreach(var item in _myService.CallSomethingReturningAsyncStream(name))
{
yield return item;
}
}
I would like to be able to in case of an ArgumentException return something like "Bad request" response.
If I try using try-catch block, I get error:
CS1626: Cannot yield a value in the body of a try block with a catch
clause
Please note that it is an API endpoint method, so error handling should ideally be in the same method, without need for making additional middlewares.
If needed, rough implementation of CallSomethingReturningAsyncStream method:
public async IAsyncEnumerable<dynamic> CallSomethingReturningAsyncStream(string name)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Name is missing", nameof(name));
(...)
foreach (var item in result)
{
yield return item;
}
}
You could install the System.Interactive.Async package, and do this:
[HttpGet("api/query")]
public IAsyncEnumerable<dynamic> Query(string name)
{
return AsyncEnumerableEx.Defer(() => _myService.CallSomethingReturningAsyncStream(name))
.Catch<dynamic, Exception>(ex =>
AsyncEnumerableEx.Return<dynamic>($"Bad request: {ex.Message}"));
}
The signature of the Defer operator:
// Returns an async-enumerable sequence that invokes the specified
// factory function whenever a new observer subscribes.
public static IAsyncEnumerable<TSource> Defer<TSource>(
Func<IAsyncEnumerable<TSource>> factory)
The signature of the Catch operator:
// Continues an async-enumerable sequence that is terminated by
// an exception of the specified type with the async-enumerable sequence
// produced by the handler.
public static IAsyncEnumerable<TSource> Catch<TSource, TException>(
this IAsyncEnumerable<TSource> source,
Func<TException, IAsyncEnumerable<TSource>> handler);
The signature of the Return operator:
// Returns an async-enumerable sequence that contains a single element.
public static IAsyncEnumerable<TValue> Return<TValue>(TValue value)
The Defer might seem superficial, but it is needed for the case that the _myService.CallSomethingReturningAsyncStream throws synchronously. In case this method is implemented as an async iterator, it will never throw synchronously, so you could omit the Defer.
You have to split method. Extract part which does async processing:
private async IAsyncEnumerable<dynamic> ProcessData(TypeOfYourData data)
{
await foreach(var item in data)
{
yield return item;
}
}
And then in API method do:
[HttpGet("api/query")]
public IActionResult Query(string name)
{
TypeOfYourData data;
try {
data = _myService.CallSomethingReturningAsyncStream(name);
}
catch (...) {
// do what you need
return BadRequest();
}
return Ok(ProcessData(data));
}
Or actually you can just move the whole thing into separate method:
[HttpGet("api/query")]
public IActionResult Query(string name)
{
try {
return Ok(TheMethodYouMovedYourCurrentCodeTo);
}
catch (...) {
// do what you need
return BadRequest();
}
}
It will of course only catch exceptions thrown before actual async enumeration starts, but that's fine for your use case as I understand. Returning bad request after async enumeration has started is not possible, because response is already being sent to client.
Update: you mentioned your service code is:
public async IAsyncEnumerable<dynamic> CallSomethingReturningAsyncStream(string name)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Name is missing", nameof(name));
foreach (var item in result)
{
yield return item;
}
}
It has the same problem as your current api method. It's generally a good practice to split such methods in two - first part performs validation, and then returns the second part. For example let's consider this code:
public class Program {
static void Main() {
try {
CallSomethingReturningAsyncStream(null);
}
catch (Exception ex) {
Console.WriteLine(ex);
}
}
public static async IAsyncEnumerable<string> CallSomethingReturningAsyncStream(string name) {
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Name is missing", nameof(name));
await Task.Delay(100);
yield return "1";
}
}
It will complete without any exceptions, because the code inside CallSomethingReturningAsyncStream was converted by compiler into a state machine and will only execute when you enumerate the result, including part which validates arguments.
However, if you change your method like this:
public class Program {
static void Main() {
try {
CallSomethingReturningAsyncStream(null);
}
catch (Exception ex) {
Console.WriteLine(ex);
}
}
public static IAsyncEnumerable<string> CallSomethingReturningAsyncStream(string name) {
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Name is missing", nameof(name));
return CallSomethingReturningAsyncStreamInternal(name);
}
// you can also put that inside method above,
// as local function
private static async IAsyncEnumerable<string> CallSomethingReturningAsyncStreamInternal(string name) {
await Task.Delay(100);
yield return "1";
}
}
It will do the same thing, BUT now method validates arguments synchronously, and then goes to async part. This code will throw argument exception which will be caught.
So I'd suggest to follow this best practice for your code, and that will also resolve your issue stated in question. There is no reason to delay throwing exception until enumeration occurs when it's clear right away that arguments are invalid and enumeration is not possible anyway.

Best practice for many try-catch blocks

I have many statements that can throw exceptions. Exceptions are not important.
My system accepts missing patient data fields.
patientData.PatientId = message.Find(...);
try
{
patientData.Gender = message.Find(...);
}
catch
{
// no gender, no problem
}
try
{
patientData.DateOfBirth = message.Find(...);
}
catch
{
// no DateOfBirth, no problem
}
// and many other try-catch blocks ...
What is the best way to write these statements that can throw exceptions but not critical?
In your example, you can create static TryFindMessage method in addition to Find. In case of exception just return false.
For example:
bool Message.TryFind(..., Message message, out string result).
But, if you wish some generic approach which you can use with anything, then you can take advantage of delegates and create some static helper.
You can use Action or Func in that case. Add another extension method which accepts Func if you need to return some value from executed function.
public static class SilentRunner
{
public static void Run(Action action, Action<Exception> onErrorHandler)
{
try
{
action();
}
catch (Exception e)
{
onErrorHandler(e);
}
}
public static T Run<T>(Func<T> func, Action<Exception> onErrorHandler)
{
try
{
return func();
}
catch (Exception e)
{
onErrorHandler(e);
}
return default(T);
}
}
And then use it so:
SilentRunner.Run(
() => DoSomething(someObject),
ex => DoSomethingElse(someObject, ex));
In case of Func, you can take result as well:
var result = SilentRunner.Run(
() => DoSomething(someObject),
ex => DoSomethingElse(someObject, ex));
I have many statements that can throw exceptions. Exceptions are not
important.
This can be dangerous, because exceptions can be thrown for different reasons, not only because of missing property.
I would advise instead of swallowing exception, modify code to return default value(null) when value is not found.
You can introduce new method in message class, for example FindOrDefault
// Pseudo code
public T FindOrDefault(string property)
{
return CanFind(property) ? Find(property) : default(T);
}
Usage of such method will be self explanatory
patientData.PatientId = message.Find(...); // Mandatory - must throw if not found
patientData.Gender = message.FindOrDefault(...);
patientData.DateOfBirth = message.FindOrDefault(...);

Xamarin app crashes at function with dynamic parameter return

I'm trying to make an exception interceptor in my Xamarin application. Right now I'm juste trying to intercept service's methods: the call from view model to buisiness logic (all in one project, full .net standard 2).
I fall upon this answer (using autofac) and found it simple and clever. It works fine, I add a try-catch to get my exception, so far so good.
But then I tried to return my exception in a DTO object type. All our services return a Task of a DTO class derived from a DTOBase abstract class. Theses classes just hold a reference to the value(s) and a IEnumerable of exception named Errors.
So basically, I try to catch the exception, put it in the list of Errors and return my object. I finished with this code :
public class ExceptionInterceptorBehaviour : IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
var method = invocation.MethodInvocationTarget;
var isAsync = method.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null;
if (isAsync && typeof(Task).IsAssignableFrom(method.ReturnType))
{
invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue);
}
}
private static async Task InterceptAsync(Task task)
{
await task.ConfigureAwait(false);
}
private static async Task<T> InterceptAsync<T>(Task<T> task)
{
try
{
T result = await task.ConfigureAwait(false);
return result;
}
catch (Exception e)
{
if (typeof(DTOBase).IsAssignableFrom(typeof(T)))
{
var ret = Activator.CreateInstance(typeof(T));
(ret as DTOBase).Errors.Add(e);
return (T)ret;
}
throw e;
}
}
}
My probleme is that the application crashes at the return of Task<T> InterceptAsync<T>(Task<T> task). No exception is raised, no pause mode in the debugger just a plain crash.
I suspect a segmentation error, but my cast does work (I tested it) and I do return a Task<T> and assign it to a Task<T> object.
Am I missing something? I don't get why it crashes like that.
Is that happening on iOS? Xamarin has some limitations defined by its underlying platforms. Dynamic code is one of them. Avoid using dynamic.
So, I took account of rubo's answer and rewrite my code with no dynamic variable and end up with this :
public class ExceptionInterceptorBehaviour : IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
var method = invocation.MethodInvocationTarget;
var isAsync = method.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null;
if (isAsync && typeof(Task).IsAssignableFrom(method.ReturnType))
{
if (method.ReturnType.IsGenericType)
{
invocation.ReturnValue = typeof(ExceptionInterceptorBehaviour)
.GetMethod("InterceptGenericAsync", BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(method.ReturnType.GenericTypeArguments[0])
.Invoke(this, new object[] { invocation.ReturnValue });
}
else
{
invocation.ReturnValue = InterceptAsync((Task)invocation.ReturnValue);
}
}
}
private async Task InterceptAsync(Task task)
{
await task.ConfigureAwait(false);
}
private async Task<T> InterceptGenericAsync<T>(Task<T> task)
{
try
{
object result = await task.ConfigureAwait(false);
return (T)result;
}
catch (Exception e)
{
if (typeof(DTOBase).IsAssignableFrom(typeof(T)))
{
var ret = Activator.CreateInstance(typeof(T));
(ret as DTOBase).Errors.Add(e);
return (T)ret;
}
throw e;
}
}
}
The fun fact is that code was still crashing when I tried to step out of InterceptGenericAsync in debug, but it works just fine if I let it run, which is weird and scary.
I did not test this solution on iOS though, I'm not sure it's working.

Handle exception with lambda async, exception not caught

im refactoring out some try catch blocks with a ExcecuteWithLogging method, but exception that occurs in restHandler.GetResultAsync() never get caught, why?
public async Task<Aaa> SearchByPersonnummerAsync(string personnummer)
{
var restHandler = new RestHandler();
GenericResult<Aaa> aaa= await ExcecuteWithLogging(async () =>
{
var res = await restHandler.GetResultAsync<Aaa>(
_configurationManager.GetSetting("api"),
"url");
return res;
});
return aaa.Result;
}
private T ExcecuteWithLogging<T>(Func<T> function)
{
try
{
function();
}
catch (Exception ex)
{
var message = ex.Message;
// Log
}
return default(T);
}
Here is my solution which is working as i prefered:
var aaa = null;
await ExcecuteWithLogging(async () =>
aaa = await Method());
public async Task<string> ExcecuteWithLogging(Func<Task> action)
{
try
{
await action();
}
catch (Exception ex)
{
// handle exception
return null;
}
return "ok";
}
I had a similar problem and here is my solution. In my case I couldn't be sure about the functions type, so I simply created an overload for async ones:
public static T CallAndLogErrors<T, TLog>(Func<T> method, ILogger<TLog> logger)
{
try
{
return method();
}
catch (Exception e)
{
LogError(e, logger);
throw;
}
}
public static async Task<T> CallAndLogErrors<T, TLog>(Func<Task<T>> method, ILogger<TLog> logger)
{
try
{
return await method();
}
catch (Exception e)
{
LogError(e, logger);
throw;
}
}
This makes it possible to call methods with T, Task<T> or async Task<T> return types like:
var result = CallAndLogErrors(() => Method(), logger);
var result = await CallAndLogErrors(() => Method(), logger);
And all exceptions are catched.
The problem is that you are not implementing an overload of ExecuteWithLogging that resolves specifically for Func<Task<T>>. Remember that lambda expressions can be converted into any compatible delegate, that is why your async lambda is assigned succesfully to your current ExecuteWithLogging method.
Before you read the rest of the answer pleas have a look to the following article, it helped me a lot when I came across with exactly the same problem that you are facing now. I am pretty much sure that after reading that article you will understand what is happening here, but for completness sake here you have some samples that will make things clarer. I have modified your methodos in the following way:
public static async void SearchByPersonnummerAsync(string personnummer)
{
var aaa = await ExcecuteWithLogging(async () =>
{
Console.WriteLine("Executing function");
var res = await Task.Run(() =>
{
Thread.Sleep(100);
Console.WriteLine("Before crashing");
throw new Exception();
return 1;
});
Console.WriteLine("Finishing execution");
return res;
});
}
private static T ExcecuteWithLogging<T>(Func<T> function)
{
try
{
Console.WriteLine("Before calling function");
function();
Console.WriteLine("After calling function");
}
catch (Exception ex)
{
var message = ex.Message;
Console.WriteLine(message);
}
Console.WriteLine("Returning..");
return default(T);
}
This is just where you are now, I only added some console logging code, what is the output?
As you can see ExecutingWithLogging is returning when the async delegate has not even crashed!
Let's add an overload that accepts a Func<Task<T>>
private static async Task<T> ExcecuteWithLogging<T>(Func<Task<T>> function)
{
T result;
try
{
Console.WriteLine("Before calling function");
result = await function();
Console.WriteLine("After calling function");
}
catch (Exception ex)
{
var message = ex.Message;
Console.WriteLine(message);
return default(T);
}
Console.WriteLine("Returning..");
return result;
}
The compiler will pick this now, from the article mentioned above:
The compiler prefers the method call that expects a delegate that returns a Task. That’s good, because it’s the one you’d rather use. The compiler comes to this conclusion using the type inference rules about the return value of anonymous delegates. The “inferred return type” of any async anonymous function is assumed to be a Task. Knowing that the anonymous function represented by the lambda returns a Task, the overload of Task.Run() that has the Func argument is the better match.
Now the output is:
And you get your exeption catched. So what is the moral? Again, I quote the mentioned article:
First, avoid using async lambdas as arguments to methods that expect Action and don’t provide an overload that expects a Func. If you do that, you’ll create an async void lambda. The compiler will happily assume that’s what you want.
Second, if you author methods that take delegates as arguments,
consider whether programmers may wish to use an async lambda as that
argument. If so, create an overload that uses Func as the
argument in addition to Action. As a corollary, create an overload
that takes Func> in addition to Task for arguments that
return a value.
EDIT
As #Servy points out in the comments if you mantain only the original version of ExecuteWithLogging T is actually a Task<Aaa>... but the only way I can think of awaiting without the overload is:
private static async Task<T> ExcecuteWithLogging<T>(Func<T> function)
{
try
{
var result = function();
if (result is Task t)
{
return await t;
}
return result;
}
catch (Exception ex)
{
var message = ex.Message;
Console.WriteLine(message);
}
Console.WriteLine("Returning..");
return default(T);
}
But besides being ugly IMHO, it always fails since the call to function() runs synchronously and by the time you want to await the task has already ended or crashed. But the worst part of this approach is that it does not even compile, the compiler complains with: Can not implicitly convert type void to T

How to simplify or wrap exceptions when rewriting synchronous code to use TPL

Given an implementation as follows:
public class SomeServiceWrapper
{
public string GetSomeString()
{
try
{
//Do Something
}
catch (IOException e)
{
throw new ServiceWrapperException("Some Context", e);
}
catch (WebException e)
{
throw new ServiceWrapperException("Some Context", e);
}
}
}
The intention of the above is to enable the consumer of GetSomeString to only need to catch ServiceWrapperException.
Consider the following approach to extending this with a similar async behaviour:
public Task<string> GetSomeStringAsync()
{
Task<string>.Factory doSomething = ...
return doSomething.ContinueWith(x =>
{
if (x.IsFaulted)
{
if (x.Exception.InnerExceptions.Count() > 1)
{
throw new AggregateException(x.Exception);
}
var firstException = x.Exception.InnerExceptions[0];
if (typeof(firstException) == typeof(IOException)
|| typeof(firstException) == typeof(WebException))
{
throw new ServiceWrapperException("Some Context", firstException);
}
}
return x.Result;
}
}
This synchronous approach to wrapping exceptions doesn't fit naturally with the asynchronous approach.
What could the author of SomeServiceWrapper do to simplify the exception handling code of any consumers so they only need to handle TradeLoaderException instead of both IOException and WebException?
I made an extension method that pretty much does that. Usage:
public static Task<string> GetSomeStringAsync()
{
var doSomething = Task.Factory.StartNew(() => "bar");
return doSomething.WrapExceptions(typeof(IOException), typeof(WebException));
}
You can just return the original task with the continuation.
I would suggest changing ServiceWrapperException to hold more than one exception like AggregateException and then change the first part.
The Method:
public static Task<TResult> WrapExceptions<TResult>(this Task<TResult> task, params Type[] exceptionTypes)
{
return task.ContinueWith(_ =>
{
if (_.Status == TaskStatus.RanToCompletion) return _.Result;
if (_.Exception.InnerExceptions.Count > 1)
{
throw new AggregateException(_.Exception);
}
var innerException = _.Exception.InnerExceptions[0];
if (exceptionTypes.Contains(innerException.GetType()))
{
throw new ServiceWrapperException("Some Context", innerException);
}
throw _.Exception;
});
}

Categories

Resources