Asynchronous Operations: General Information

An instance of a graph is created on each round trip to process a request created by the user on the appropriate form. After the request is processed, the graph instance must be cleared from the memory of the MYOB Acumatica server. If you implement code that might require a long time to execute an action or to process a document or data, you should execute this code asynchronously in a separate thread.

In this chapter, you will learn how to run an operation asynchronously by using the PXLongOperation class.

Learning Objectives

In this chapter, you will learn how to do the following:

  • Implement a long-running action
  • Create the associated button on the table toolbar for the long-running action
  • Set up the long-running action to execute asynchronously by using the PXLongOperation class

Applicable Scenarios

You set up an operation to run asynchronously when this operation is expected to take a long time to finish its execution.

Use of the PXLongOperation Class

To make the system invoke the method in a separate thread, you can use the PXLongOperation.StartOperation method. Within the method that you pass to StartOperation, you can, for example, create a new instance of a graph and invoke a processing method on that instance. The following code snippet demonstrates how you can execute code asynchronously as a long-running operation in a method of a graph.

Important:
To instantiate graphs from code, use the PXGraph.CreateInstance<T>() method. Do not use the new T() graph constructor because in this case, no extensions or overrides of the graph are initialized.
public class MyGraph : PXGraph
{
  ...
  public void MyMethod()
  {
    ...
    PXLongOperation.StartOperation(this, delegate()
    {
      // insert the delegate method code here
      ...
      GraphName graph = PXGraph.CreateInstance<GraphName>();
      foreach (... in ...)
      {
        ...
      }
      ...
    });
  ...
  }
  ...
}
Note:
Do not pass a reference to this graph to the StartOperation method from a graph extension. Otherwise, the system cannot notify the UI about whether any errors occurred during the asynchronous operation or whether the operation was completed.

If you need to save data to the database inside a long-running operation, call the Save.Press() method of the current graph. We do not recommend that you use the Actions.PressSave() method because it performs an external call and should be used from the UI only.

The following code shows an example of a method called InvoiceOrder that is to be executed asynchronously. This method is being called within the delegate that you pass to PXLongOperation.StartOperation() method.

PXLongOperation.StartOperation(this, delegate ()
{
  InvoiceOrder(graphCopy); 
});

The PXLongOperation.StartOperation() method creates a separate thread and executes the specified delegate asynchronously on this thread. The method passed into PXLongOperation.StartOperation() matches the following delegate type, which has no input parameters.

delegate void PXToggleAsyncDelegate();
Tip:
The anonymous method definition (delegate()) is used to shorten the code in the example.

Inside the delegate() method, you should not use members of the current graph, because this would lead to synchronous execution of the method. Instead, use a copy of the graph, which you can create by using the var graphCopy = this.Clone(); statement.

Use of the Custom Information Dictionary

In the delegate method of a long-running operation, you can store a data object in the _CustomInfo dictionary of the long-running operation and get the list of records processed by the method. You can add to the dictionary any data object needed for a long-running operation by using a SetCustomInfo method.

The following diagram shows that each long-running operation includes the _CustomInfo dictionary, which can contain multiple key-value pairs with custom data.
Figure 1. Location of custom data in the memory of the MYOB Acumatica server


For a processing operation, the system stores the PXProcessingMessagesCollection<TTable> list of messages in the dictionary. Each message in the list is of the PXProcessingMessage type, which includes a string message and an error level that is of the PXErrorLevel type.

See New way to work with CustomInfo of PXLongOperation at http://asiablog.acumatica.com for more information about the use of the dictionary.