Skip to content

Latest commit

 

History

History
112 lines (84 loc) · 6 KB

File metadata and controls

112 lines (84 loc) · 6 KB

PX1120

This document describes the PX1120 diagnostic.

Summary

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

Diagnostic Description

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.

Task Type

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.

Reported Issues

The PX1120 diagnostic reports three types of incorrect uses of the Task types:

Task Instance in a Variable or Parameter

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.

Example of Incorrect Code

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

Not Awaited Task-Returning Expression

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.

Example of Incorrect Code

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

Method with the Object Return Type Returns Task

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.

Example of Incorrect Code

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

Related Articles