Showing posts with label multithread. Show all posts
Showing posts with label multithread. Show all posts

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!!






Tuesday, February 15, 2022

Multithread in sysoperation framework in D365FO

 Hi guys, Today we see the multithread in sys operation. For sys operation we required 4 classes.

Here we need to create two more classes.

1. Controller class.

2. Service class.

From controller class we need to call service class like the same of sys operation. In service class we need to apply group in our table based on requirement and we are using standard classes to send our value to our contract and controller class.

For multithread Controller class we need to create action menu item.

Just check below code.

Multithread Controller class: 

class TestMultiThreadController extends SysOperationServiceController { public ClassDescription defaultCaption() { return 'order creation using multithread'; } public static void main(Args args) { TestMultiThreadController controller = new TestMultiThreadController(); controller.startOperation(); } public void new() { //super(classStr(TestMultiThreadService), //methodStr(TestMultiThreadService, processOperation), //SysOperationExecutionMode::Synchronous); super(); this.parmClassName(classStr(TestMultiThreadService)); this.parmMethodName(methodStr(TestMultiThreadService, processOperation)); this.parmDialogCaption("Batch operation"); } }

--------------------------------------------------------------------------

Multithread Service Class :


class TestMultiThreadService extends SysOperationServiceBase { public void processOperation()//TestContract _contract) { TestCustomer testCustomer;//My table TestMyController controller;//my controller TestMyContract contract;//my contarct BatchHeader batchHeader; BatchInfo batchInfo; SysRecurrenceData sysRecurrenceData = SysRecurrence::defaultRecurrence(); batchHeader = BatchHeader::construct(); controller = new TestMyController(); while select testCustomer group by Filename // 'Filename' my custom field. where testCustomer.Processed == NoYes::No && testCustomer.ErrorInRecord == NoYes::No { sysRecurrenceData = sysRecurrence::setRecurrenceEndAfter(sysRecurrenceData, 1); batchInfo         = controller.batchInfo(); batchInfo.parmCaption(strFmt("order creation for File- %1", testCustomer.FileName)); contract = controller.getDataContractObject();//get my contract class. contract.parmFileName(testCustomer.FileName);// passing my field value. batchHeader.addTask(controller);//Passing my cotroller to the batch. batchHeader.parmRecurrenceData(sysRecurrenceData); batchHeader.save(); } } }

Keep Daxing!!