'Catch exception' extension on Task class - c#

I have code like this:
try { await myFunc(); }
catch (MyException ex)
{
switch (ex.Code)
{
case 1: ... break;
case 2: ... break;
...
}
}
And was wondering is it possible to make all that look something like this:
...
{
await myFunc().HandleMyExeptions(OnMyException);
}
private void OnMyException(int exCode)
{
switch (ex.Code)
{
case 1: ... break;
case 2: ... break;
...
}
}
where I would create extension class for tasks something like this
public static class TaskExtensions
{
public static void HandleErrors(this Task task, Action<int> handleError)
{
// I don't know what would go here nor whether the header of
// this function is a step in a good direction
}
}

It's going to look something like this:
public static Task HandleErrors(this Task task, Action<int> handleError) =>
task.ContinueWith(t =>
{
if (t.Status == TaskStatus.Faulted && t.Exception.InnerException is MyException ex)
{
handleError(ex.Code);
}
});
Had you provided sample code in your question that showed how your existing exception handling worked, then I would have fully tested this, but you didn't. If you care to write that code and add it to your question, then I'll do the testing.
A simpler approach using await:
public static async Task HandleErrors(this Task task, Action<int> handleError)
{
try
{
await task;
}
catch (MyException ex)
{
handleError(ex.Code);
}
}

Related

Async Lambda Issue

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();
}

How to use action delegate to avoid if else

I have the following code:
public class NotificationService {
private readonly Dictionary<NotificationMessageType, Action<IList<RecipientDetail>, NotificationMessageType>> _actionMap;
public NotificationService() [
_actionMap = new Dictionary<NotificationMessageType, Action<IList<RecipientDetail>, NotificationMessageType>> ();
_actionMap.Add(NotificationMessageType.SessionBookedReminder, new Action<IList<RecipientDetail>, NotificationMessageType>(GenerateNotificationsAsync)); //getting errror here because of async await
}
public async Task GenerateNotificationsAsync(IList<RecipientDetail> usersToNotify, NotificationMessageType messageType)
{
Would like to avoid if else here:
if(messageType == NotificationMessageType.SessionBookedReminder)
{
await Task.WhenAll(usersToNotify.Select(u => GenerateBookingNotificationAsync(u, messageType)).ToArray());
}
else
{
await Task.WhenAll(usersToNotify.Select(u => SendNotificationAsync(u, messageType)).ToArray());
}
}
public async Task GenerateNotificationsAsync(IList<RecipientDetail> usersToNotify, NotificationMessageType messageType)
{
}
public async Task GenerateBookingNotificationAsync(RecipientDetail userToNotify, NotificationMessageType messageType)
{
}
}
How can I use action delegate to avoid if else. I've tried with following, but getting error due to async await.
Can anyone help how to do the right way?
Thanks
Your dictionary must match the method declaration, your methods returns a task, so your
Action<IList<RecipientDetail>, NotificationMessageType>
Must be changed to something like
Func<IList<RecipientDetail>, NotificationMessageType,Task>
your method must return a task in order to use it in a async/await manner

Wrapping a Task to return a value

I feel like there is a better way to do the following and looking for other opinions.
Essentially, I am trying to utilize the async/await pattern but need to return a bool value from method to indicate what happened in the method which is causing me to wrap the Task with Task so this can be accomplished. My spidey-sense is telling me something is wrong with my implementation.
In the below example "LongRunningTask" is something I don't have control over because it is a library method that returns a Task, so something I can't change. The remainder of the flow logic could be changed. Obviously, this is just a fictional representation of my real-issue but meant to demonstrate the issue, so don't get caught-up in the hard-coded "GetData", etc...
Take a look and let me know of other options.
void Main()
{
StartApplication();
}
private async void StartApplication()
{
// This is a just fictional example passing hard-coded GetData
var didExecuteLongRunningTask = await ProcessStuff("GetData");
if (didExecuteLongRunningTask)
{
Console.WriteLine("Long running task was executed");
}
else {
Console.WriteLine("Long running task was NOT executed");
}
}
// Define other methods and classes here
private async Task<bool> ProcessStuff(string command)
{
if (command == "GetData")
{
await LongRunningTask();
return await Task<bool>.Factory.StartNew(() => true);
}
else
{
return await Task<bool>.Factory.StartNew(() => false);
}
}
private Task LongRunningTask()
{
return Task.Delay(2000);
}
Yes, you are right, you are over-complicating it. You can just do:
private async Task<bool> ProcessStuff(string command)
{
if (command == "GetData")
{
await LongRunningTask();
return true;
}
else
{
return false;
}
}
You can look at the MSDN for more information: Asynchronous Programming

How to prevent an action parameter from being an async lambda?

I am writing a small wrapper (MyWrapper) for use in unit tests. Its purpose is to wrap test code with a try-catch in order to catch one specific exception (MySpecialException) and then ignore the test.
Why I do that should not be relevant for this question.
Given the code below, how do I prevent others from passing an Action and using async like this?
Or in other words: How do I force them to use MyWrapper.ExecuteAsync(Func<Task>) instead?
using System;
using System.Threading.Tasks;
using NUnit.Framework;
namespace PreventAsyncActionLambdaExample
{
[TestFixture]
public class Example
{
[Test]
public async Task ExampleTest()
{
// How do I prevent others from passing an action and using async like this?
// Or in other words: How do I force them to use MyWrapper.ExecuteAsync(Func<Task>) instead?
MyWrapper.Execute(async () =>
{
var cut = new ClassUnderTest();
await cut.DoSomethingAsync();
Assert.Fail("Problem: This line will never be reached");
});
}
}
public static class MyWrapper
{
// This method SHOULD NOT, BUT WILL be used in this example
public static void Execute(Action action)
{
try
{
action();
}
catch (MySpecialException)
{
Assert.Ignore("Ignored due to MySpecialException");
}
}
// This method SHOULD BE USED in this example, BUT WILL NOT be used.
public static async Task ExecuteAsync(Func<Task> func)
{
try
{
await func();
}
catch (MySpecialException)
{
Assert.Ignore("Ignored due to MySpecialException");
}
}
}
public class MySpecialException : Exception
{
// This is another exception in reality which is not relevant for this example
}
public class ClassUnderTest
{
public Task DoSomethingAsync()
{
return Task.Delay(20); // Simulate some work
}
}
}
I am afraid that you can't really prevent this at compile-time but you could write another overload that will be picked-up in this case to tell them that they are supposed to use ExecuteAsync instead:
public static Task Execute(Func<Task> action)
{
throw new Exception("Please use the ExecuteAsync(Func<Task> func) method instead if you will be passing async lambdas");
}
As mentioned in other answers, I do not think you can prevent it in compile time. However, you can do a hacky workaround and throw an exception. Inspired by this answer. It might not be a good solution, but it could at least make the test fail.
public static bool IsThisAsync(Action action)
{
return action.Method.IsDefined(typeof(AsyncStateMachineAttribute),
false);
}
// This method SHOULD NOT, BUT WILL be used in this example
public static void Execute(Action action)
{
try
{
if (IsThisAsync(action))
{
Console.WriteLine("Is async");
throw new ArgumentException("Action cannot be async.", nameof(action));
}
else
{
Console.WriteLine("Is not async");
}
action();
}
catch (MySpecialException)
{
}
}
And tests:
[TestClass]
public class MyWrapperTests
{
// Will not pass
[TestMethod]
public void ShouldAllowAsyncAction()
{
// This will throw an exception
MyWrapper.Execute(async () =>
{
Assert.IsTrue(true);
await Task.Run(() =>
{
Console.WriteLine("Kind of async");
});
});
}
// Will pass, since ArgumentException is expected.
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void ShouldThrowArgumentExceptionWhenAsync()
{
// This will throw an exception. But that's expected.
MyWrapper.Execute(async () =>
{
Assert.IsTrue(true);
await Task.Run(() =>
{
Console.WriteLine("Kind of async");
});
});
}
// Passes
[TestMethod]
public void ShouldAllowSyncAction()
{
MyWrapper.Execute(() =>
{
Assert.IsTrue(true);
});
}
}

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