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.
{
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
Post a Comment