-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Analyzer/fixer suggestion]: Exceptions thrown inside async methods should be wrapped by Task.FromException #70905
Comments
Tagging subscribers to this area: @dotnet/area-system-threading-tasks Issue DetailsSparked from this question: #70574 (comment) Exceptions thrown inside methods that return If the method is marked as Suggested category: Reliability ExamplesMethod that returns TaskBefore// Task
private Task MyMethodAsync(string arg, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrEmpty(arg);
if (/* some condition */)
{
throw new InvalidOperationException("Some message");
}
return SomethingAsync(cancellationToken);
}
// async Task
private async Task MyMethodAsync(string arg, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrEmpty(arg);
if (/* some condition */)
{
throw new InvalidOperationException("Some message");
}
await SomethingAsync(cancellationToken).ConfigureAwait(false);
} After// Task
private Task MyMethodAsync(string arg, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrEmpty(arg); // Do not change this
if (/* some condition */)
{
return Task.FromException(new InvalidOperationException("Some message")); // wrap this
}
return SomethingAsync(cancellationToken);
}
// async Task
private Task MyMethodAsync(string arg, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrEmpty(arg); // Do not change this
if (/* some condition */)
{
// wrap this, await it, but don't add ConfigureAwait(false), that should be added by CA2007 separately
await Task.FromException(new InvalidOperationException("Some message"));
}
await SomethingAsync(cancellationToken).ConfigureAwait(false);
} Method that returns ValueTaskBefore// ValueTask
private ValueTask MyMethodAsync(string arg, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrEmpty(arg);
if (/* some condition */)
{
throw new InvalidOperationException("Some message");
}
return SomethingAsync(cancellationToken);
}
// async ValueTask
private async ValueTask MyMethodAsync(string arg, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrEmpty(arg);
if (/* some condition */)
{
throw new InvalidOperationException("Some message");
}
await SomethingAsync(cancellationToken).ConfigureAwait(false);
} Afterprivate ValueTask MyMethodAsync(string arg, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrEmpty(arg); // Do not change this
if (/* some condition */)
{
return ValueTask.FromException(new InvalidOperationException("Some message")); // wrap this
}
return SomethingAsync(cancellationToken);
}
// async ValueTask
private async ValueTask MyMethodAsync(string arg, CancellationToken cancellationToken)
{
ArgumentException.ThrowIfNullOrEmpty(arg);
if (/* some condition */)
{
// wrap this, await it, but don't add ConfigureAwait(false), that should be added by CA2007 separately
await ValueTask.FromException(new InvalidOperationException("Some message"));
}
await SomethingAsync(cancellationToken).ConfigureAwait(false);
} cc @buyaa-n
|
If the method is built with the
|
Seems like there could be an analyzer for this. Or does that already exist? |
I've marked this as ready for review, but I suspect we're going to find it's a bit noisy. If this is approved, whoever tackles it will likely need to do a bit of work to tune the rule to get it to the point where it can be on by default in runtime. |
(It's also very likely to have a lot of false negatives, due to exceptions getting thrown indirectly via method calls.) |
Category: Reliability |
One could just as easily argue that it's bad and may catch users off guard, and require them to handle both asynchronous and synchronous exceptions, instead of just asynchronous. Which in fact are two bullet points that come up in other async guidance communicated for ASP.NET Core applications by e.g. David Fowl, in the context of preferring Setting up wrapping methods that synchronously throw on argument exceptions also fall afoul of exactly this guidance, because the way it's implemented is a task-returning method which is not async-await, but delegates to one that is. I.e. there's no cut and dry single point of view that applies here. |
Sparked from this question: #70574 (comment)
Exceptions thrown inside methods that return
Task
orValueTask
should be wrapped byTask.FromException
, except if they areArgumentNullException
orArgumentException
.If the method is marked as
async
, we could alsoawait
theTask.FromException
.Suggested category: Reliability
Suggested level: Warning (Same as CA2007)
Suggested milestone: 8.0
Examples
Method that returns Task
Before
After
Method that returns ValueTask
Before
After
cc @buyaa-n
The text was updated successfully, but these errors were encountered: