Bypassing Table Validations During Data Entity Imports using a Context Class

The Problem

When importing data via Data Entities (DIXF or OData), the system often triggers validateWrite() on the underlying tables. While these validations are essential for UI-based entries, they can sometimes block bulk imports or perform redundant checks that slow down the process. We need a way to tell the table: "Hey, I'm currently being called by a Data Entity, so skip this specific check."

The Solution: A Scoped Context Class

The solution involves three parts: a Context Class to hold the state, an Entity Override to set the state, and a Table Override to read the state.

Step 1: Create the Context Class

This class implements System.IDisposable so it can be used in a using block. This ensures that the global instance is cleared (disposed) as soon as the import operation finishes, preventing "state leakage" to other processes.

public final class RebateAgreementHeaderEntityContext implements System.IDisposable
{
    private static RebateAgreementHeaderEntityContext instance;
    private boolean isDataImporting;
    // Standard Singleton/Context pattern
    public void dispose()
    {
        instance = null;
    }
    private void new()
    {
        if (instance)
        {
            throw error(Error::wrongUseOfFunction(funcName()));
        }
        instance = this;
    }
    public static RebateAgreementHeaderEntityContext construct()
    {
        return new RebateAgreementHeaderEntityContext();
    }
    public static RebateAgreementHeaderEntityContext current()
    {
        return instance;
    }
    public boolean parmDataImporting(boolean _dataImporting = isDataImporting)
    {
        isDataImporting = _dataImporting;
        return isDataImporting;
    }
}

Step 2: Initialize the Context in the Data Entity

We override insertEntityDataSource on the Data Entity. By wrapping the next call in a using block, the context is active only for the duration of that specific insert operation.

public boolean insertEntityDataSource(DataEntityRuntimeContext _entityCtx, DataEntityDataSourceRuntimeContext _dataSourceCtx)

{

    boolean ret;


    // The 'using' block ensures dispose() is called automatically at the '}'

    using (RebateAgreementHeaderEntityContext context = RebateAgreementHeaderEntityContext::construct())

    {

        context.parmDataImporting(true);

          

        // Any table-level code called inside this 'next' will see the context

        ret = next insertEntityDataSource(_entityCtx, _dataSourceCtx);

    }                

    

    return ret;

}


Step 3: Consume the Context in the Table Level

In the table's validateWrite(), we check if a context exists and if isDataImporting is true.

public boolean validateWrite()

{

    boolean ret = next validateWrite();

    // Check if we are running within our custom context

    RebateAgreementHeaderEntityContext context = RebateAgreementHeaderEntityContext::current();    

    boolean isImporting = (context && context.parmDataImporting());

    if (ret && !isImporting)

    {

        // Only perform this validation if NOT importing via the entity

        if (this.SomeField == "")

        {

            ret = checkFailed("Validation failed for manual entry.");

        }

    }

    return ret;

}



Comments