Fallback to the LINQ to Objects Mode

With LINQ, you may not be able to filter records by using custom C# functions. For example, suppose that your C# function filters records by a regular expression, which cannot be converted to standard SQL functions. If the system cannot convert a custom C# function in a LINQ statement to an SQL query tree, the system falls back to LINQ to Objects mode—that is, the system executes the data query in memory, which can lead to degradation of the application's performance.

The following code shows an example of a situation when the system falls back to LINQ to Objects mode. In this example, the system selects from the database all records from the CRCase table, and then, in memory, orders the retrieved records by the Date column and selects the records that satisfy the condition specified by using the MyHelpers.IsHighPriority function.
// MyHelpers.IsHighPriority is a custom function.
var results = graph
    .Select<CRCase>()
    .OrderByDescending(c => c.Date)
    .Where(c => MyHelpers.IsHighPriority(c));

foreach (CRCase case in results)
{
    ...
}
Note: If the system falls back to LINQ to Objects, only the results of the base PXSelectBase query are merged with PXCache as described in Merge of the Records with PXCache. The Merge and ReadOnly methods do not affect the merge of records with PXCache for the queries that caused fallback.
The LINQ fallback is supported in MYOB Acumatica Framework for compatibility with previous versions. The system writes to the trace log about all situations in which the system falls back to LINQ to Objects mode. Therefore, we strongly recommend that you investigate the trace log for such issues and fix the issues in one of the following ways:
  • Remove the custom C# functions that cause fallback so that the full query is executed in the database.
  • Append the AsEnumerable() method to the part of the query that can be converted to SQL, and add after it the conditions that include custom C# functions. In this case, the system does not waste resources trying to build the SQL query tree for the whole query. Instead, the system builds the SQL query tree for the part of the query that has AsEnumerable() appended and performs the corresponding request to the database, while the custom C# conditions of the query are processed in memory. For example, the code example above can be modified as follows.
    // MyHelpers.IsHighPriority is a custom function.
    var results = graph
        .Select<CRCase>()
        .OrderByDescending(c => c.Date).AsEnumerable()
        .Where(c => MyHelpers.IsHighPriority(c));
    
    foreach (CRCase case in results)
    {
        ...
    }
    Note: The system executes the following SQL query for the code above, where [list of columns] is the list of columns of the CRCase table.
    SELECT [list of columns] FROM CRCase
        ORDER BY CRCase.Date DESC