Batch parallelism or multithreading in Dynamics 365 for Finance and Operations
1. Create a contract class to define the container of the item
Notes: Do not use a list. It may cause the error: “no metadata class defined for data contract object” when the function’s running.
Code:
[DataContractAttribute]
public class ABC_MaterialBalanceContract
{
container conItem;
[DataMemberAttribute, SysOperationControlVisibilityAttribute(false)]
public container parmConItem(container _conItem = conItem)
{
conItem = _conItem;
return conItem;
}
}
2. Create a service class to process extends SysOperationServiceBase
3. Create a controller class to call the service class (step 2) extends SysOperationServiceController
Code:
class ABC_MaterialbalanceController extends SysOperationServiceController
{
protected void new()
{
super();
this.parmClassName(classStr(ABC_MaterialbalanceService));
this.parmMethodName(methodStr(ABC_MaterialbalanceService, processOperation));
this.parmDialogCaption("@ABC:GenerateMaterialBalance");
}
public static ABC_MaterialbalanceController construct()
{
ABC_MaterialbalanceController controller = new ABC_MaterialbalanceController();
return controller;
}
public static void main(Args args)
{
ABC_MaterialbalanceController::construct().startOperation();
}
public ClassDescription defaultCaption()
{
return "@ABC:GenerateMaterialBalance";
}
}
4. Create a contract class to define the number of tasks
Code:
[DataContractAttribute]
class ABC_ScheduleMaterialBalanceContract
{
private int tasksNumber;
[DataMemberAttribute]
public int parmParallelTasksNumber(int _tasksNumber = tasksNumber)
{
tasksNumber = _tasksNumber;
return tasksNumber;
}
}
5. Create a service class to separate the task
- Create 1 temp table to contain all items that will be processed.
- Insert data into the temp table.
- Separate the number of items per task based on the number of tasks that the user input.
- I have one task (process for non-material) and it will be processed after all task of material item has finished. I will use the method addDependency of BatchHeader class.
Code:
public class ABC_ScheduleMaterialBalanceService extends SysOperationServiceBase
{
int numberOfTask;
ABC_MaterialBalanceTmp materialBalanceTmp;
public void processOperation(ABC_ScheduleMaterialBalanceContract _dataContract)
{
int eachRecordCount, numberOfOpenRecord, numberOfBundleSize;
ABC_BOMParmLineHistoryView bomParmLineHistoryView;
ItemId itemId;
FromDate fromDateId;
ToDate toDateId;
Query q = new Query();
QueryRun qr;
QueryBuildDataSource qbds;
List listItem;
container conItem;
ABC_MaterialBalanceContract contract;
BatchHeader batchHeader;
ABC_MaterialbalanceController controller;
ABC_NonMaterialbalanceController nonMaterialController;
ABC_MaterialBalance materialBalance;;
delete_from materialBalance;
numberOfTask = _dataContract.parmParallelTasksNumber();
numberOfOpenRecord = this.countMaterialItem();
if (numberOfOpenRecord > 0 && numberOfTask > 0)
{
// how many bundle will run thay will given by user.
numberOfBundleSize = numberOfOpenRecord div numberOfTask;
batchHeader = BatchHeader::construct();
nonMaterialController = ABC_NonMaterialbalanceController::construct();
batchHeader.addRuntimeTask(nonMaterialController, this.getCurrentBatchTask().RecId);
while select itemId from materialBalanceTmp
{
eachRecordCount++;
if (eachRecordCount == 1)
{
conItem = conNull();
}
conItem += [materialBalanceTmp.ItemId];
if (eachRecordCount == numberOfBundleSize)
{
controller = ABC_MaterialbalanceController::construct();
contract = controller.getDataContractObject();
contract.parmConItem(conItem);
batchHeader.addRuntimeTask(controller, this.getCurrentBatchTask().RecId);
batchHeader.addDependency(nonMaterialController, controller, BatchDependencyStatus::Finished);
eachRecordCount = 0;
}
}
if (eachRecordCount > 0)
{
controller = ABC_MaterialbalanceController::construct();
contract = controller.getDataContractObject();
contract.parmConItem(conItem);
batchHeader.addRuntimeTask
(controller, this.getCurrentBatchTask().RecId);
batchHeader.addDependency
(nonMaterialController, controller, BatchDependencyStatus::Finished);
}
batchHeader.save();
}
}
private int countMaterialItem()
{
Query q = new Query();
QueryRun qr;
QueryBuildDataSource qbds;
ABC_BOMParmLineHistoryView bomParmLineHistoryView;
InventTable inventTable;
RecordInsertList recordInsertList =
new RecordInsertList(tableNum
(ABC_MaterialBalanceTmp)
, false, false, false, false, false, materialBalanceTmp);
int numberOfOpenRecord;
qbds = q.addDataSource(tableNum(ABC_BOMParmLineHistoryView));
qbds.orderMode(ordermode::GroupBy);
qbds.addSortField(fieldnum
(ABC_BOMParmLineHistoryView,ItemId));
qbds.addSortField(fieldnum
(ABC_BOMParmLineHistoryView,InventUnitId));
qbds.addSortField(fieldnum
(ABC_BOMParmLineHistoryView,BOMUnitId));
qbds.addSortField(fieldnum
(ABC_BOMParmLineHistoryView,RetailVariantId));
qbds.addSortField(fieldnum
(ABC_BOMParmLineHistoryView,Name));
qbds.addSortField(fieldnum
(ABC_BOMParmLineHistoryView,Description));
qbds.addSelectionField(fieldNum
(ABC_BOMParmLineHistoryView, ItemId));
qbds.addSelectionField(fieldNum
(ABC_BOMParmLineHistoryView, InventUnitId));
qbds.addSelectionField(fieldNum
(ABC_BOMParmLineHistoryView, BOMUnitId));
qbds.addSelectionField(fieldNum
(ABC_BOMParmLineHistoryView, RetailVariantId));
qbds.addSelectionField(fieldNum
(ABC_BOMParmLineHistoryView, Name));
qbds.addSelectionField(fieldNum
(ABC_BOMParmLineHistoryView, Description));
qbds.addSelectionField(fieldNum
(ABC_BOMParmLineHistoryView, RemainTransferQty),SelectionField::Sum);
qr = new QueryRun(q);
while(qr.next())
{
bomParmLineHistoryView =
qr.get(tablenum(ABC_BOMParmLineHistoryView));
inventTable = InventTable::find( bomParmLineHistoryView.ItemId);
if (inventTable.ItemType !=ItemType::Service)
{
materialBalanceTmp.clear();
materialBalanceTmp.ItemId = bomParmLineHistoryView.ItemId;
numberOfOpenRecord++;
recordInsertList.add(materialBalanceTmp);
}
}
recordInsertList.insertDatabase();
return numberOfOpenRecord;
}
}
6. Create a controller class to call the service class (step 5) extends SysOperationServiceController
7. Create a menu item for the class controller (step 6)
Comments
Post a Comment