Running Batch Jobs in X++
Batch jobs are the backbone of any D365FO implementation. This post walks through two approaches: the legacy RunBase pattern and the modern SysOperation framework.
RunBase pattern
The RunBase class is the classic way to build a batch-capable process. It handles the dialog, packing/unpacking parameters for the batch queue, and progress updates.
class MyBatchJob extends RunBase
{
// Parameters stored for batch execution
CustAccount custAccount;
public boolean canGoBatch()
{
return true;
}
public Object dialog()
{
DialogField fldCustAccount;
Dialog dlg = super();
fldCustAccount = dlg.addField(extendedTypeStr(CustAccount));
return dlg;
}
public boolean getFromDialog()
{
custAccount = fldCustAccount.value();
return super();
}
public void run()
{
// Your business logic here
info(strFmt("Processing customer: %1", custAccount));
}
public container pack()
{
return [custAccount];
}
public boolean unpack(container packedClass)
{
[custAccount] = packedClass;
return true;
}
public static void main(Args _args)
{
MyBatchJob job = new MyBatchJob();
if (job.prompt())
{
job.runOperationNow();
}
}
}
SysOperation framework
SysOperation separates concerns into a Service class, a Data Contract, and a Controller. It’s more verbose but far more testable and composable.
// 1. Data contract — defines parameters
[DataContractAttribute]
class MyServiceContract
{
CustAccount custAccount;
[DataMemberAttribute('CustAccount')]
public CustAccount parmCustAccount(CustAccount _custAccount = custAccount)
{
custAccount = _custAccount;
return custAccount;
}
}
// 2. Service — the actual business logic
class MyService
{
public void process(MyServiceContract _contract)
{
info(strFmt("Processing: %1", _contract.parmCustAccount()));
}
}
Key differences
| RunBase | SysOperation | |
|---|---|---|
| Testability | Hard (dialog coupling) | Easy (contract is a POCO) |
| Parallelism | Manual | Built-in via SysOperationServiceController |
| Recommended for | Legacy/simple | All new development |
For new development, always reach for SysOperation. Reserve RunBase for modifying existing legacy code.