Wednesday, February 22, 2023

Multithread based on user count in sysoperation framework in D365FO

 Multithread based on user count in sys operation framework in D365FO.

  • The system will create multiple tasks in the same batch job id.
  • Based on user input system will create multiple threads.

Multithread Contract Class :

[DataContractAttribute]
class TestMultiThreadContract
{
    private int    tasksNumber;

    [DataMemberAttribute('Count')]
    public int parmParallelTasksNumber(int _tasksNumber = tasksNumber)
    {
	tasksNumber = _tasksNumber;
	return tasksNumber;
    }
}


Multithread Controller class: 

class TestMultiThreadController extends SysOperationServiceController
{
    protected void new()
    {
        super();

        this.parmClassName(classStr(openSalesOrderService));
        this.parmMethodName(methodStr(openSalesOrderService, createScheduleTask));
        this.parmExecutionMode(SysOperationExecutionMode::ScheduledBatch);
        this.parmDialogCaption("Sales order process");
    }

    public static TestMultiThreadController construct()
    {
        return new TestMultiThreadController();
    }

    public static void main(Args args)
    {
        TestMultiThreadController::construct().startOperation();
    }

    public ClassDescription defaultCaption()
    {
        return "Sales order process";
    }
}


Multithread Service Class :

class TestMultiThreadService extends SysOperationServiceBase
{
    int                         numberOfOpenRecord;
    int                         numberOfBatchThread;
    FromDate                    fromDateFilter;
    ToDate                      toDateFilter;
    Integer                     numberOfBundleSize;

    public void createScheduleTask(TestMultiThreadContract _contract)
    {
        Integer             eachRecordCount;
        SalesId             fromSalesId,
                            toSalesId,
                            lastSalesId;
        FromDate            fromDateId;
        ToDate              toDateId;

        if (!this.isExecutingInBatch())
        {
            throw error(Error::wrongUseOfFunction(funcName()));
        }
        
        numberOfBatchThread = _contract.parmParallelTasksNumber();
		
        numberOfOpenRecord  = this.openSalesOrderCount();

        if (numberOfOpenRecord > 0 && numberOfBatchThread > 0)
        {
            BatchHeader batchHeader = this.getCurrentBatchHeader();
			
			// how many bundle will run thay will given by user.
            numberOfBundleSize = numberOfOpenRecord div numberOfBatchThread;

            SalesTable       salesTable;

            while select SalesId from salesTable
                order by salesTable.SalesId
                where salesTable.SalesStatus == SalesStatus::Backorder
            {
                eachRecordCount++;

                if (eachRecordCount == 1)
                {
                    fromSalesId = salesTable.SalesId;
                }

                if (eachRecordCount == numberOfBundleSize)
                {
                    toSalesId = salesTable.SalesId;

                    TestContract 	contract;
                   
                    var controller = SysOperationServiceController((classStr(TestMyService),
								    methodStr(TestMyService, processOperation),
								    SysOperationExecutionMode::Synchronous));
					 contract = controller.getDataContractObject();
					 contract.parmFromSalesOrderNum(fromSalesId);
                     contract.parmToSalesOrderNum(toSalesId);																			
																								
                    batchHeader.addRuntimeTask(controller, this.getCurrentBatchTask().RecId);
                    
                    eachRecordCount = 0;
                }

                lastSalesId = salesTable.SalesId;
            }

            if (eachRecordCount > 0)
            {
                toSalesId = lastSalesId;

                TestContract 	contract;
                   
		var controller = SysOperationServiceController((classStr(TestMyService),
								methodStr(TestMyService, processOperation),
								SysOperationExecutionMode::Synchronous));
		contract = controller.getDataContractObject();
		contract.parmFromSalesOrderNum(fromSalesId);
		contract.parmToSalesOrderNum(toSalesId);																			
																							
		batchHeader.addRuntimeTask(controller, this.getCurrentBatchTask().RecId);
            }

            batchHeader.save();
        }
    }

    private int openSalesOrderCount()
    {
        SalesTable          salesTable;

        select count(RecId) from salesTable
            where salesTable.SalesStatus == SalesStatus::Backorder;

	numberOfOpenRecord = int642int(salesTable.RecId);

        return numberOfOpenRecord;
    }
}


My Service Class :

class TestMyService extends SysOperationServiceBase
{
    public void AutoPostProcessExecute(TestContract _contract)
    {
        #OCCRetryCount
        if (!this.isExecutingInBatch())
        {
            throw error(Error::wrongUseOfFunction(funcName()));
        }

        SalesId             fromSalesId     = _contract.parmFromSalesOrderNum();
        SalesId             toSalesId       = _contract.parmToSalesOrderNum();


        SalesTable      salesTable;
        int             counter;

        while select salesTable
            order by salesTable.SalesId
            where salesTable.SalesId >= fromSalesId
            && salesTable.SalesId <= toSalesId
            && salesTable.SalesStatus == SalesStatus::Backorder
        {
	    try
	    {
		ttsbegin;
		this.insertSalesOrderIntoCustomTable(salesTable);
		counter++;
		ttscommit;
	    }
	    catch (Exception::Deadlock)
	    {
	    	retry;
	    }
	    catch (Exception::UpdateConflict)
	    {
		if (appl.ttsLevel() == 0)
		{
	    	    if (xSession::currentRetryCount() >= #RetryNum)
		    {
			throw Exception::UpdateConflictNotRecovered;
		    }
		    else
		    {
			retry;
		    }
		}
		else
		{
		    throw Exception::UpdateConflict;
		}
	    }
	    catch(Exception::DuplicateKeyException)
	    {
		if (appl.ttsLevel() == 0)
		{
	    	    if (xSession::currentRetryCount() >= #RetryNum)
		    {
			throw Exception::DuplicateKeyExceptionNotRecovered;
		    }
		    else
		    {
			retry;
		    }
		}
		else
		{
		    throw Exception::DuplicateKeyException;
		}
	    }
	    catch (Exception::Error)
	    {
		infolog.add(Exception::Error, strFmt("", SalesTable.SalesId));
							
		continue;
	    }
	    catch (Exception::CLRError)
	    {
		System.Exception ex = CLRInterop::getLastException();
		Infolog.add(Exception::CLRError,  ex.ToString() );

		continue;
	    }
        }
    }
    info(strFmt("%1 Sales order invoiced", counter));
}

 
    private void insertSalesOrderIntoCustomTable(SalesTable _salesTable)
    {
	MycustomTable	mycustomTable;
		
	mycustomTable.salesId 		= _salesTable.salesId;
	mycustomTable.CustomField1 	= _salesTable.CustomField1;
	mycustomTable.CustomField2 	= _salesTable.CustomField2;
	mycustomTable.insert();
    }
}


My ContractClass :

[DataContractAttribute]
class TestContract
{
    private SalesId    fromSalesId;
    private SalesId    toSalesId;

    [DataMemberAttribute]
    public SalesId parmFromSalesOrderNum(SalesId _fromSalesId = fromSalesId)
    {
        fromSalesId = _fromSalesId;
        return fromSalesId;
    }

    [DataMemberAttribute]
    public SalesId parmToSalesOrderNum(SalesId _toSalesId = toSalesId)
    {
        toSalesId = _toSalesId;
        return toSalesId;
    }
}


Using RunBase Batch Click here

Keep Daxing!!






No comments:

Post a Comment