According to the Microsoft Docs:
There are two common use cases for local functions: public iterator methods and public async methods. Both types of methods generate code that reports errors later than programmers might expect.
...
The technique can be employed with async methods to ensure that exceptions arising from argument validation are thrown before the asynchronous work begins:
public Task<string> PerformLongRunningWork(string address, int index, string name)
{
if (string.IsNullOrWhiteSpace(address))
throw new ArgumentException(message: "An address is required", paramName: nameof(address));
if (index < 0)
throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));
return longRunningWorkImplementation();
async Task<string> longRunningWorkImplementation()
{
var interimResult = await FirstWork(address);
var secondResult = await SecondStep(index, name);
return $"The results are {interimResult} and {secondResult}. Enjoy.";
}
}
I don't really understand this example. If we just get rid of the local function and extract its contents to the outer scope and put them right after the validation, won't we achieve the same result? What does the local function actually give in this case?
If it actually doesn't improve anything, can you come up with a better example?
There's a difference between:
Call PerformLongRunningWork and it throws an exception.
Call PerformLongRunningWork and it successfully executes, and gives you back a Task<string> which contains an exception.
That is:
Task<string> task;
try
{
task = PerformLongRunningOperation();
}
catch (Exception e)
{
// PerformLongRunningOperation itself threw
}
bool containsException = task.IsFaulted;
try
{
string result = await task;
}
catch (Exception e)
{
// The Task<string> returned from PerformLongRunningWork contained an exception
}
If you throw an exception from an async Task method, that exception is wrapped up inside the Task which is returned.
Therefore, your example with a non-async method which delegates to an async local function will throw those ArgumentException directly when it's called, and not return them wrapped up in the Task<string> it returns.
If you rewrote the example to remove the local function and instead make PerformLongRunningWork async, then those ArgumentExceptions would be wrapped up inside the Task<string> returned.
Which one you want to do is a matter of debate.
One of the benefits: I don't have to pass local variables and parameters as parameters to the other method since they are already captured.
Another use case that I benefit from frequently is working with TaskCompletionSource<T>. An example of that:
public Task<T> DoSomeWorkAsync()
{
TaskCompletionSource<T> completionSource = new TaskCompletionSource<T>();
SomeBackgroundWorker worker = new SomeBackgroundWorker();
worker.OnWorkComplete += workComplete;
worker.DoSomeWorkInABackgroundThread();
return completionSource.Task;
void workComplete(T workResult)
{
worker.OnWorkComplete -= workComplete;
completionSource.SetResult(workResult);
}
}
Related
What is the best way to return a completed Task object?
It is possible to write Task.Delay(0), or Task.FromResult<bool>(true) whatever.
But what is the most efficient way?
Answer from Stephen Toub (MSFT):
If you want a new Task object each time, Task.FromResult is the most
efficient. Task.Delay(0) in its current implementation will return a
cached task, but that's an implementation detail. If you want to use
a cached task, you should cache one yourself, e.g. private static
readonly Task s_completedTask = Task.FromResult(true); and then use
s_completedTask.
Task.FromResult would be the most direct. It also includes inbuilt results for a few common integers etc. However, if your value is not an "obvious" value (and won't have inbuilt handling) but is likely to be returned often in your scenario - then you can create your own cached result in a field (maybe static if appropriate) - but it is important to cache the Task, not the result itself.l - otherwise just use Task.FromResult each time.
Here's a little demo which shows the difference in exception handling between methods marked and not marked with async.
public Task<string> GetToken1WithoutAsync() => throw new Exception("Ex1!");
// Warning: This async method lacks 'await' operators and will run synchronously. Consider ...
public async Task<string> GetToken2WithAsync() => throw new Exception("Ex2!");
public string GetToken3Throws() => throw new Exception("Ex3!");
public async Task<string> GetToken3WithAsync() => await Task.Run(GetToken3Throws);
public async Task<string> GetToken4WithAsync() { throw new Exception("Ex4!"); return await Task.FromResult("X");}
public static async Task Main(string[] args)
{
var p = new Program();
try { var task1 = p.GetToken1WithoutAsync(); }
catch( Exception ) { Console.WriteLine("Throws before await.");};
var task2 = p.GetToken2WithAsync(); // Does not throw;
try { var token2 = await task2; }
catch( Exception ) { Console.WriteLine("Throws on await.");};
var task3 = p.GetToken3WithAsync(); // Does not throw;
try { var token3 = await task3; }
catch( Exception ) { Console.WriteLine("Throws on await.");};
var task4 = p.GetToken4WithAsync(); // Does not throw;
try { var token4 = await task4; }
catch( Exception ) { Console.WriteLine("Throws on await.");};
}
// .NETCoreApp,Version=v3.0
Throws before await.
Throws on await.
Throws on await.
Throws on await.
Moved (and edited) from When async Task<T> required by interface, how to get return variable without compiler warning)
I think that I am a little late. However, I still want to share this answer, for an updated version.
If you want to return a completed task, you should use the following:
return Task.CompletedTask;
This will also automatically set the status to RanToCompletion.
I'm designing some code right now where I'm throwing an exception if a string parameter is null or empty and the exception is thrown as it should be, but it isn't getting caught when I'm UnitTesting.
Here's the client I'm using.
public class PipeClient : IPipeClient
{
public async void Send(string host, string pipeName, Message msg)
{
if (string.IsNullOrEmpty(msg.PreparedMessage))
throw new ArgumentException("MESSAGE_NOT_FOUND");
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(pipeName))
throw new ArgumentNullException();
if (!host.TryParseHost())
throw new ArgumentException("INVALID_HOST_NAME");
using (var pipeClient = new NamedPipeClientStream(host, pipeName, PipeDirection.Out))
{
pipeClient.Connect(200);
using (var writer = new StreamWriter(pipeClient))
{
await Task.Run(() => writer.WriteLine(msg.PreparedMessage));
writer.Flush();
}
}
}
}
And here's the UnitTest
[TestMethod]
public void Send_FailsOnWrongHostName()
{
var name = "FailWithHostname";
var msg = new Message(MyStates.Register, "UnitTest", "Test");
try
{
var client = new PipeClient();
client.Send("lol", name, msg);
}
catch (Exception e)
{
Assert.IsTrue(e is ArgumentException);
}
}
So when I run that test it should as far as I know throw the exception when I call the the Send method (which is does) and then get caught in the catch clause because I'm not catching it inside the PipeClient. Yet it doesn't, it just exits with a failed test.
If you need any more information just let me know, thanks in advance.
there's a few things I want to raise in this answer. I'm not sure of your experience level so please don't think I'm being condescending at any point.
Firstly a brief note on async methods and Tasks.
Async void should be avoided unless in an async event handler. Async methods should return Task or Task otherwise there is nothing for the calling method to keep hold of to know when the method is done and to report back whether the method threw an exception. Async void is essentially fire and forget, there is no one left to observe the exceptions.
"In observed Tasks no one can you scream" -Me ,2018
Exceptions thrown in async methods are nicely unwrapped and thrown
when the async method is awaited, with the call stack all preserved
and reasonably sensible. If you don't await the result eventually at
some point in the future you will get an UnobservedTaskException
that, if you haven't configured a global handler for, will bring down
your application. If you get the result of an async method
synchronously using .Wait() or .Result or via
.GetAwaiter().GetResult() (all 3 you should try and avoid but the 3rd
option is best if you have to I have been informed), then you will
get the original exception wrapped in an AggregateException.
Now if none of this is making much sense to you, I would recommend doing some reading up Tasks and async/await.
Now onto your Test.
Your method is async void so there is nothing for the calling method to have returned to it to represent the work or to let it know that the method has thrown an exception. So it carries on, the test finishes and then everything completes with no exceptions because the UnobservedTaskException can be thrown at anypoint in the future (I think it is related to when the garbage collector tidies up the faulted Task and then it throws and because the garbage collector is non-deterministic we can't say when that will happen)
So what if you made your async method return a Task??? Well that's still not quite right. You are now returning a Task that will be in a faulted state because of the exception, however because you never await it, the exception is never 'unwrapped' and actually thrown and so you're test happily continues.
What you need to do is make your Test async and return a Task and make the method you're testing async Task not async void and await that method in your test.
Like this
[TestMethod]
public async Task Send_FailsOnWrongHostName()
{
var name = "FailWithHostname";
var msg = new Message(MyStates.Register, "UnitTest", "Test");
try
{
var client = new PipeClient();
await client.Send("lol", name, msg);
}
catch (Exception e)
{
Assert.IsTrue(e is ArgumentException);
}
}
public class PipeClient : IPipeClient
{
public async Task Send(string host, string pipeName, Message msg)
{
if (string.IsNullOrEmpty(msg.PreparedMessage))
throw new ArgumentException("MESSAGE_NOT_FOUND");
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(pipeName))
throw new ArgumentNullException();
if (!host.TryParseHost())
throw new ArgumentException("INVALID_HOST_NAME");
using (var pipeClient = new NamedPipeClientStream(host, pipeName, PipeDirection.Out))
{
pipeClient.Connect(200);
using (var writer = new StreamWriter(pipeClient))
{
await Task.Run(() => writer.WriteLine(msg.PreparedMessage));
writer.Flush();
}
}
}
}
I have a strange problem combining the async/await to make it work:
I created a small procedure, which should handle basically the try/catch of every action:
internal static void HandledAction(Action action, Info infoBar)
{
try
{
action();
}
catch (Exception ex)
{
infoBar.SetError("An Exception occured: " + ex.Message);
WriteLog(ex.StackTrace);
}
Nohing to fancy, but it's worth since changing error-handling is very easy made.
But what happens, if I'd like to get Data async in the Lambda? Lets take this simple example:
private void mnuImportData_Click(object sender, RoutedEventArgs e)
{
ActionHelper.HandledAction(async () =>
{
throw new NotImplementedException("Ups");
}, infoMain);
}
Sure, the HandledAction gets called, passes, since it gets the pointer back, and the exception gets thrown, of course not handled.
I imagine I have to create a AsyncHandledAction, and set the action async, but is there a easier way to solve this problem?
I guess many people use a central exception-handling, and there are far better solutions for this?
Thanks in advance
Matthias
Edit: I created an example, which should shpw netter I need: I basically dont want the whole Action I pass being awaitable, but one call in the Lambda is:
ActionHelper.HandledActionAsync(() =>
{
//elided
CheckFileResult rslt = await excelImport.CheckFilesAsync(tmpPath);
//elided
}, infoMain);
Of course, by doing so, I get the error:
Error 3 The 'await' operator can only be used within an async lambda expression. Consider marking this lambda expression with the 'async' modifier.
The reason is: Action instead of Func. Since yours:
async () =>
{
throw new NotImplementedException("Ups");
}
in fact is:
async void Method() { }
when Func<Task> is:
async Task Method() { }
Async void will capture SynchronizationContext.Current and when exception is thrown it will be posted to SynchronizationContext by SynchronizationContext.Post() - (in windows runtime you can catch these types of exception). In case of ASP.NET/Console application SynchronizationContext.Current returns null which means that exception would be propagated to Thread Pool and I'm not sure but I think it is not possible to catch it. However when there is returned Task by asynchronous method exception could be marshaled to the caller through this returned Task. It is also worth mention that async lambda expresions will always prefer methods with Func<Task> over Action. General rule is: never use async void (async Action) unless it is "top-level method" (in example event handler).
You need an async version of HandleAction
internal static async Task HandledAction(Func<Task> action, Info infoBar)
{
try
{
await action();
}
catch (Exception ex)
{
infoBar.SetError("An Exception occured: " + ex.Message);
WriteLog(ex.StackTrace);
}
}
of course you should call the method with await
private async void mnuImportData_Click(object sender, RoutedEventArgs e)
{
await ActionHelper.HandledAction(async () =>
{
throw new NotImplementedException("Ups");
}, infoMain);
}
I'm writing a class which have synchronous and asynchronous versions of the same method
void MyMethod(object argument) and Task MyMethodAsync(object argument). In sync version I validate argument with the simple check
if (argument == null)
throw new ArgumentNullException("argument");
How should the same check look like in async method?
1) Same as for sync method
2) (Updated after first answer)
if (argument == null)
return new Task.Factory.StartNew(() => { throw new ArgumentNullException("argument"); });
That depends a bit on when you want the error to be raised - i.e. eagerly, or as part of the awaitable. As with iterator blocks, if you want eager error checks, you need two methods, for example:
public Task<int> SomeMethod(..args..) {
if(..args fail..) throw new InvalidOperationException(...);
return SomeMethodImpl(...args...);
}
private async Task<int> SomeMethodImpl(...args...)
{
... await etc ...
}
This will then perform any argument checking as part of the initial call, not the awaitable. If you want the exception to be part of the awaitable, you can just throw it:
public async Task<int> SomeMethod(..args..) {
if(..args fail..) throw new InvalidOperationException(...);
... await etc ...
}
However, in your example, the fact that you are returning a Task suggests that this is not actually an async method, but is an async (but not async) method. You can't just do:
return new Task(() => { throw new ArgumentNullException("argument"); });
because that Task will never have been started - and never will be. I suspect you would need to do something like:
try {
throw new InvalidArgumentException(...); // need to throw to get stacktrace
} catch(Exception ex) {
var source = new TaskCompletionSource<int>();
source.SetException(ex);
return source.Task;
}
which is... a bit of a mouthful and could probably be encapsulated a bit better. This will return a Task that indicates it is in the Faulted state.
Starting from C# 7.0, you can use use local function to reduce noise in code but still be compliant with argument check practice from sonar rule S4457.
For example, this code will throw an ArgumentNullException in both cases: if you call it with await or without.
private Task WaitSeconds(int? durationInSeconds)
{
if(durationInSeconds == null) throw new ArgumentNullException(nameof(durationInSeconds));
async Task WaitSecondsInternal()
{
await Task.Delay(TimeSpan.FromSeconds(durationInSeconds.Value));
}
return WaitSecondsInternal();
}
Simply throw it like you did in the sync method, the TPL has various mechanisms in place to re-throw exception, for example when you read the .Result property or the access .Exception property.
As per sonar rule S4457
Because of the way async/await methods are rewritten by the compiler, any exceptions thrown during the parameters check will happen only when the task is observed. That could happen far away from the source of the buggy code or never happen for fire-and-forget tasks.
Therefore it is recommended to split the method into two: an outer method handling the parameter checks (without being async/await) and an inner method to handle the iterator block with the async/await pattern.
This rule raises an issue when an async method throws any exception derived from ArgumentException and contains await keyword.
Noncompliant Code Example
public static async Task SkipLinesAsync(this TextReader reader, int linesToSkip) // Noncompliant
{
if (reader == null) { throw new ArgumentNullException(nameof(reader)); }
if (linesToSkip < 0) { throw new ArgumentOutOfRangeException(nameof(linesToSkip)); }
for (var i = 0; i < linesToSkip; ++i)
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line == null) { break; }
}
}
Compliant Solution
public static Task SkipLinesAsync(this TextReader reader, int linesToSkip)
{
if (reader == null) { throw new ArgumentNullException(nameof(reader)); }
if (linesToSkip < 0) { throw new ArgumentOutOfRangeException(nameof(linesToSkip)); }
return reader.SkipLinesInternalAsync(linesToSkip);
}
private static async Task SkipLinesInternalAsync(this TextReader reader, int linesToSkip)
{
for (var i = 0; i < linesToSkip; ++i)
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line == null) { break; }
}
}
Update from the future: TL;DR to catch expressions in async methods you have to await, Task.WaitAll, or .Result.
I created a somewhat convoluted async method that just runs other async methods. You can disregard most of it as only the line var mSpekTask... is of interest, also, I don't care about the logic, I only want to know where my exception went. My main problem is that ex.ToString() is never hit even though inside mSpecTask an exception definitly happens.
public async Task LoadAsync(IEnumerable<ProductRequest> feed, int? customerId,
IProgress<int> mSpecProgress, Action<Task> mSpecCompletionHandler)
{
var ids = feed.Select(x => x.ProductId.ToString()).Distinct().ToList();
try
{
var mSpecTask = this.LoadMSpecAsync(mSpecProgress, ids);
}
catch (Exception ex)
{
ex.ToString();
}
}
Here is the code for LoadMSpecAsync
public Task<ResultSet> LoadMSpecAsync(IProgress<int> prg, IEnumerable<string> ids)
{
return this.LoadAsync(prg, ids, Selector.M, SPMS, x => x.Order);
}
Here is the code for LoadAsync, await db.ExecuteTVP(progress, spName, ids, parameters) generates an exception.
private async Task<Dictionary<Pair, dynamic>> LoadAsync(IProgress<int> progress,
IEnumerable<string> ids, Selector s, string spName, Func<dynamic, int> k,
Func<dynamic, dynamic> f = null, object parameters = null)
{
parameters = new ExpandoObject().CopyFromSafe(parameters);
if (spName != SPMAP) ((dynamic)parameters).lang = this.languageCode;
using (var db = new SqlConnection(this.connectionString))
{
await db.OpenAsync();
var results = await db.ExecuteTVP(progress, spName, ids, parameters);
db.Close();
}
return this.data[s];
}
When an async method throws an exception, that exception is placed on the returned Task. It's not raised directly to the caller. This is by design.
So, you have to either await the Task returned from LoadMSpecAsync or have your mSpecCompletionHandler examine its Task argument for exceptions. It will show up there.
You can handle unobserved Task exceptions as follows:
TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) =>
{
eventArgs.SetObserved();
((AggregateException)eventArgs.Exception).Handle(ex =>
{
//TODO: inspect type and handle exception
return true;
});
};
I'm going to add an answer to my own question because there's a useful piece of information that I found out. The intermediary method LoadMSpecAsync is swalloing the exception. For this not to happen it needs a little teak. You need to add the async keyword before the return type and the "await" keyword after "return".
I had an Exception being swallowed by myTask.Wait() (or WaitAsync), but it was a task-nesting issue. That is, when I did the myTask.Wait(), myTask had a passed-in parameter Task that ALSO called myParameterTask.Wait(). The parameter task had its own Exception-catch and no throw, so the originally thrown Exception wasn't propagating back to the calling thread.
I thought there was an issue with myTask.Wait() throwing Exceptions, but it wasn't.