To describe the result of any business operation,
I have the following Result class
public class Result<TResponse>
{
public TResponse? Response { get; set; }
public Exception? Exception { get; set; }
internal Result(TResponse response, Exception exception)
{
Response = response;
Exception = exception;
}
public static implicit operator Result<TResponse>(TResponse response)
{
return new Result<TResponse>(response, default);
}
public static implicit operator Result<TResponse>(Exception e)
{
return new Result<TResponse>(default, e);
}
//TODO other codes that I want to ask
}
It overloaded operators for TResponse and Exception
So I can use the following code happily:
public Result<int> Method1()
{
try
{
//do_some_sync_stuff()
return 42;//will return Result<int>(42,null)
}
catch (Exception e)
{
return e;//will return Result<int>(null,e)
}
}
Now I want to use async version of the latest code like that:
public async Result<int> Method2()
{
try
{
//await do_some_async_stuff()
return 42;//example value
}
catch (Exception e)
{
return e;
}
}
But dotnet gets angry and says:
error CS1983: The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T>
Is there a way to achive that ?
I know I can use Task<Result<int>> but it is way more complicated to use like that- nested generics
I have tried to add GetAwaiter and implement AsyncMethodBuilder but can't success -returning exceptions do not work
Related links but not solving exactly that issue:
link1 link2 link3
If you want to do a async processing in your method you need to have a return type like Task. Because the compiler its doing a cast like
Result<int> = Task<Result<int>>
because of the async method.
If you add the Task return type, the cast should work fine.
Related
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.
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(...);
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
I have this method:
public object LongRunningTask()
{
return SomethingThatTakesTooLong();
}
I wrote the following code so I can transform a normal method in an async one and still get the Exception:
public async Task<object> LongRunningTaskAsync()
{
Exception ex = null;
object ret = await Task.Run(() =>
{
object r = null;
try
{
//The actual body of the method
r = SomethingThatTakesTooLong();
}
catch (Exception e)
{
ex = e;
}
return r;
});
if (ex == null)
return ret;
else
throw ex;
}
When I need to do this in several methods, I have to copy all this code and change only the middle.
Is there a way to do something like this?
[SomeAttributeThatDoesThatMagically]
public async Task<object> LongRunningTaskAsync()
{
return SomethingThatTakesTooLong();
}
Attributes are generally metadata though it is possible to define attributes that can be executed (such as security behaviours in WCF) however, something has to be looking for it first. Your attributes won't just magically run.
I suspect you might have to use a dynamic proxy.
Take a look at how WCF does things for ideas.
There are a lot of common bool TryXXX(out T result) methods in the .NET BCL, the most popular being, probably, int.TryParse(...).
I would like to implement an async TryXXX() method. Obviously, I can't use out parameters.
Is there an established pattern for this?
More to the point, I need to download and parse a file. It's possible that the file does not exist.
This is what I came up with so far:
public async Task<DownloadResult> TryDownloadAndParse(string fileUri)
{
try
{
result = await DownloadAndParse(fileUri); //defined elsewhere
return new DownloadResult {IsFound = true, Value = result}
}
catch (DownloadNotFoundException ex)
{
return new DownloadResult {IsFound = false, Value = null}
}
//let any other exception pass
}
public struct DownloadResult
{
public bool IsFound { get; set; }
public ParsedFile Value { get; set; }
}
I've come up with the following definitions. The defaultValue parameters are there mostly to be able to overload the TryGet method, as generic constraints are not part of a method's signature, that which makes the method unique when deciding which method to call (for instance, the return type is also not part of the signature).
public async Task<T> TryGet<T>(Func<Task<T>> func, T defaultValue = null) where T : class
{
try
{
return await func();
}
catch (ArgumentException)
{
return defaultValue;
}
catch (FormatException)
{
return defaultValue;
}
catch (OverflowException)
{
return defaultValue;
}
}
public async Task<Nullable<T>> TryGet<T>(Func<Task<T>> func, Nullable<T> defaultValue = null) where T : struct
{
try
{
return await func();
}
catch (ArgumentException)
{
return defaultValue;
}
catch (FormatException)
{
return defaultValue;
}
catch (OverflowException)
{
return defaultValue;
}
}
You should review exception handling, this example handles the common parsing exceptions. It may make more sense to react to other exceptions, such as InvalidOperationException and NotSupportedException, probably the most used exception types on the framework itself (not necessarily the most commonly thrown ones).
Another approach is to re-throw critical exceptions, such as ThreadAbortException and have a simple catch-all clause that returns the default value. However, this will hide every exception not deemed critical, no matter how severe it is.
As such, and because throwing exceptions is an expensive operation, it's Parse that is usually defined in terms of TryParse. So your TryGet should have a contract, e.g. deal with OperationCanceledException, which includes TaskCanceledException and nothing else.
Finally, you should name it TryGetAsync, following the Async-suffix convention. [1] [2]
One of possible decisions is an array of ParsedFile, containing 0 or 1 element.
public async Task<ParsedFile[]> TryDownloadAndParse(string fileUri)
{
try
{
return new[] { await DownloadAndParse(fileUri) };
}
catch (DownloadNotFoundException ex)
{
return new ParsedFile[0];
}
}
Now you can check the result:
. . .
var parsedFiles = await TryDownloadAndParse(url);
if (parsedFiles.Any())
{
var parsedFile = parsedFiles.Single();
// more processing
}
. . .
If you want to call void method, you can use ?. operator:
var parsedFiles = await TryDownloadAndParse(url);
parsedFiles.SingleOrDefault()?.DoVeryImportantWorkWithoutResult();
UPDATE
In Azure you can use ConditionalValue<TValue> class.