This document describes the PX1120 diagnostic.
| Code | Short Description | Type | Code Fix |
|---|---|---|---|
| PX1120 | Incorrect work with the Task types in the Acumatica asynchronous code. You should not store the Task instance in a local variable or parameter. The Task-typed expressions should be awaited or immediately returned, and a method returning a Task-typed expression should have the Task type as its return type. |
Warning | Unavailable |
While Acumatica Framework supports asynchronous programming using the async/await pattern, it imposes significant limitations on the asynchronous code due to specifics of the async operations implementation in Acumatica Framework.
The PX1120 diagnostic detects the uses of the Task types that are not supported by Acumatica Framework. This diagnostic helps to prevent incorrect use of the async/await pattern in Acumatica Framework that can lead to runtime issues or unexpected behavior.
The Task type is an umbrella term for several .Net Task types:
System.Threading.Tasks.Task,System.Threading.Tasks.Task<T>,System.Threading.Tasks.ValueTask,System.Threading.Tasks.ValueTask<T>
This page frequently uses terms Task and the Task type. These terms refer to all the types listed above.
The PX1120 diagnostic reports three types of incorrect uses of the Task types:
You should not store a Task instance in a local variable, field, or parameter. Complex manipulations with tasks are discouraged because they can easily lead to the loss of synchronization context in Acumatica Framework make problems difficult to diagnose.
Instead, await the task directly or return it immediately if the method signature allows it.
public class MyGraph : PXGraph<MyGraph>
{
private Task _taskField; // Incorrect: Task stored in a field
public async Task ProcessAsync(Task inputTask) // Incorrect: Task stored in a parameter
{
Task task = DelayAsync(); // Incorrect: Task stored in a variable
await task;
Task<int> taskWithResult = GetValueAsync(); // Incorrect: Task<T> stored in a variable
int result = await taskWithResult;
}
private Task DelayAsync() => Task.Delay(100);
private Task<int> GetValueAsync() => Task.FromResult(42);
}Method calls and other C# expressions that return the Task instance should be await-ed within async methods.
According to the rules described in the previous section, storing Task instances into a local variable, field, or parameter is prohibited. This means that you can only make a "fire-and-forget" operation if you don't await the task immediately.
Such operations are discouraged in general because any exception thrown during the task execution will not be observed. Unobserved exceptions will be swallowed by the runtime which can lead to silent problems in the application.
Therefore, all C# expressions that return the Task instance should be await-ed.
The only exception to this rule is when the task is immediately returned from the method. In this case, the caller is responsible for awaiting the task, and the PX1120 diagnostic will not report the expression.
public class MyGraph : PXGraph<MyGraph>
{
public async Task ProcessAsync()
{
GetValueAsync(); // Incorrect: Task<T>-returning method call is not awaited
DelayAsync().FireAndForget(); // Incorrect: Task-returning method call is not awaited
await DelayAsync() // Incorrect: Task-returning method call is not awaited. Should report diagnostic on the call to DelayAsync
.ContinueWith(t => { ... }, TaskScheduler.Default);
await Task.WhenAll(
DelayAsync(), // Incorrect: Task-returning method calls are not awaited. Should report diagnostic on calls to DelayAsync and GetValueAsync
GetValueAsync());
}
private Task<int> GetValueAsync() => Task.FromResult(42);
private Task DelayAsync() => Task.Delay(100);
}When a method returns a Task-typed expression, the method's return type should be a compatible Task type rather than object or another non-Task type.
Returning a task as a non-task type defeats the purpose of async operations and prevents proper async/await use by callers.
public class MyGraph : PXGraph<MyGraph>
{
public object GetTaskAsObject()
{
return DelayAsync(); // Incorrect: The declared return type of the method is object but the method returns Task instance
}
public object ProcessExpression() => DelayAsync(); // Incorrect: The declared return type of the method is object but the method returns Task instance
private Task DelayAsync() => Task.Delay(100);
}- Asynchronous Operations: General Information
- Asynchronous Operations: How They are Handled in Acumatica ERP
- Task
- Exceptions thrown by unobserved tasks
- Avoid Async Void
- PX1038: Async void methods, lambdas, and anonymous methods should not be used with the Acumatica Framework
- PX1099: List of async APIs banned in the Acumatica Framework