Extending RunBaseBatch Class to Create and Run a Batch

I am now ready to extend the RunBaseBatch class to create and run batches. I found the following link:

https://docs.microsoft.com/en-us/dynamicsax-2012/developer/walkthrough-extending-runbasebatch-class-to-create-and-run-a-batch

But I found that this does not apply to Dynamics 365, how to do such an operation in Dynamics?

This approach is still supported, but there is also a newer framework introduced in AX 2012: SysOperation. I recommend using this new framework for new development. It can generate dialogs for you, it handles (de)serialization, it supports several execution modes and so on.

I am going to carry out such development in Dynamics 365 Fo, does SysOperation also apply to Dynamics 365 Fo development?

https://docs.microsoft.com/zh-cn/dynamicsax-2012/developer/walkthrough-extending-runbasebatch-class-to-create-and-run-a-batch

I’m doing development by following this link, and I don’t know what to do when I reach the position below:

This step is to compile first, and then click Generate Incremental CIL. I have compiled it now, but I didn’t find a place to execute it. Click Generate Incremental CIL.

That’s a different topic. While you’ll write the class in the same way, AX 2012 and D365FO compile it differently. There is no manual CIL compilation in D365FO, because all code is compiled to CIL automatically. If you want to follow instructions for a different version of AX, you shouldn’t be surprised that not everything is applicable.

By the way, why did you decided to use the old RunBaseBatch framework instead of the SysOperation framework?

I am going to try these two different frameworks on my local site, so that I can better understand the difference between the two frameworks

I am now using RunBatch framework, after I run it, now he has the following error:

the following is my code:

 public static void main(Args _args)
    {
        BatchHeader batHeader;
        BatchInfo batInfo;
        RunBaseBatch rbbTask;
        str sParmCaption = "正在运行批处理同步产品数据, (b351)"+DateTimeUtil ::toStr( DateTimeUtil ::utcNow());
        ;
        rbbTask = new Batch4DemoClass1();
        batInfo = rbbTask .batchInfo();
        batInfo.parmCaption(sParmCaption);
        batInfo.parmGroupId(""); // The "Empty batch group".
        batHeader = BatchHeader ::construct();
        batHeader.addTask(rbbTask);
        batHeader.save();
        info(strFmt("'%1'进行测试输出, batch has been scheduled.", sParmCaption));
    }
class Batch4DemoClass1 extends RunBaseBatch
{
        
    /// <summary>
    ///
    /// </summary>
    public void run()
    {
        System.Net.HttpWebRequest       request;
        System.Net.HttpWebResponse      response;
        CLRObject   clrObj;
        System.Exception        ex;
        System.Net.WebHeaderCollection httpHeader;
        System.IO.Stream        requestStream, responseStream;
        System.IO.StreamWriter  streamWriter;
        EcoResProduct           EcoResProducts;
        EcoResProductTranslation  EcoResProductTranslations;
        INVENTTABLEMODULE       INVENTTABLEMODULEs;
        InventItemPurchSetup    InventItemPurchSetups;
        InventTable             InventTables;
        InventTable             InventTabless;
        int64 UpdateCountRows=0;

        System.Net.HttpWebRequest       requests;
        System.Net.HttpWebResponse      responses;
        CLRObject   clrObjs;
        System.Net.WebHeaderCollection httpHeaders;
        System.IO.Stream        requestStreams, responseStreams;
        System.IO.StreamWriter  streamWriters;
        ;
      
        while select ItemId,Product,isSyncToCRM from InventTables where InventTables.isSyncToCRM !="Y"
        {
            httpHeaders = new System.Net.WebHeaderCollection();
            new InteropPermission(InteropKind::ClrInterop).assert();
            clrObjs = System.Net.WebRequest::Create("This url is hidden");
                requests = clrObjs;
            requests.set_Headers(httpHeaders);
            requests.Method = "POST";
            requests.ContentType = "application/x-www-form-urlencoded";
            requestStreams = requests.GetRequestStream();
            streamWriters = new System.IO.StreamWriter(requests.GetRequestStream());
            streamWriters.Write("UserName=admin&Password=123");
            streamWriters.Flush();
            streamWriters.Close();
            responses = requests.GetResponse();
            System.IO.StreamReader streamReads = new System.IO.StreamReader(responses.GetResponseStream());
            container test =str2con(streamReads.ReadToEnd());
            str test1=conPeek(test,2);
            str test2= subStr(test1,10,2000);
            str te1='"';
            str test3=  strRem(test2,te1);

            select firstonly RecId from EcoResProducts where EcoResProducts.RecId == InventTables.Product;;
            select firstonly Description from  EcoResProductTranslations where EcoResProductTranslations.Product == EcoResProducts.RecId;
            select firstonly Price,ModuleType from INVENTTABLEMODULEs where INVENTTABLEMODULEs.ItemId== InventTables.ItemId&&INVENTTABLEMODULEs.ModuleType == 2;
            select firstonly LeadTime,LowestQty from InventItemPurchSetups where InventItemPurchSetups.ItemId == InventTables.ItemId;
            httpHeader = new System.Net.WebHeaderCollection();
            new InteropPermission(InteropKind::ClrInterop).assert();
            clrObj = System.Net.WebRequest::Create("This url is hidden");
                request = clrObj;
            httpHeader.Add("Authorization",test3);
            request.set_Headers(httpHeader);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            requestStream = request.GetRequestStream();
            streamWriter = new System.IO.StreamWriter(request.GetRequestStream());
            streamWriter.Write("name="+EcoResProductTranslations.Name+"&stocknum="+InventTables.ItemId+"&sellingprice="+int2Str(real2int(INVENTTABLEMODULEs.price))+"&purchasetime="+int2Str(InventItemPurchSetups.LeadTime)+"&replacementnum="+InventTables.AltItemId+"&new_qdl="+int2Str(real2int(InventItemPurchSetups.LowestQty))+"&describe="+EcoResProductTranslations.Description+"&productstructure=1");
            streamWriter.Flush();
            streamWriter.Close();
            response = request.GetResponse();
            System.IO.StreamReader streamRead = new System.IO.StreamReader(response.GetResponseStream());

            ttsBegin;
            select forUpdate InventTabless
                where InventTabless.ItemId == InventTables.ItemId;
            InventTabless.isSyncToCRM = "Y";
            InventTabless.update();
            ttsCommit;
            UpdateCountRows++;
        }
        info(strFmt( "产品数据已同步成功,同步了%1条数据到CRM",UpdateCountRows));
    }
    //存储批处理
    public container pack()
    {
        return conNull();
    }
    //返回批处理的存储对象以使用
    public boolean unpack(container packedClass)
    {
        return true;
    }
    //确定是在服务器还是客户端上运行
    // True-Server;
    public boolean runsImpersonated()
    {
        return true;
    }
    ////需要设置为true,以便可以在批处理中运行该类
    boolean canGoBatch()
    {
        return true;
    }

}

This part of the code can run normally without using the RunBatch framework. Is there an error in my RunBatch framework code?

Please remove your code from run() and run it again. Does it work? If so, you know that the framework works and the problem is somewhere in your code. You’ll have to debug it. I would expect that the timeout exception is coming from the web request.

Also, why are you creating a task manually? The normal implementation of main() is this:

public static void main(Args _args)
{
	Batch4DemoClass1 demo = new Batch4DemoClass1();
	if (demo.prompt())
	{
	    demo.run();
	}
}

Let’s discussed the timeout error in your other thread on the same topic, A time out error occurs when running a method on the test environment.

Is there anything else you need to know about the originally topic of this thread, i.e. how to use RunBaseBatch in D365FO? If not, could you please make the verified answer(s)?

Whether the thread can be retained first, we can continue to discuss related issues about runbasebatch later

Hello, Martin Dráb

Let’s go back to the RunBaseBatch thread again.

I found a phenomenon, when I use the following two different pieces of code to call the code of RunBaseBatch, there will be different results

When using the following piece of code to call my calss, a timeout error will appear after my class loop is executed 100 times.

        BatchHeader batHeader;
        BatchInfo batInfo;
        RunBaseBatch rbbTask;
        str sParmCaption = "正在运行批处理同步产品数据, (b351)"+DateTimeUtil ::toStr( DateTimeUtil ::utcNow());
        ;
        rbbTask = new Batch4DemoClass1();
        batInfo = rbbTask .batchInfo();
        batInfo.parmCaption(sParmCaption);
        batInfo.parmGroupId(""); // The "Empty batch group".
        batHeader = BatchHeader ::construct();
        batHeader.addTask(rbbTask);
        batHeader.save();
        info(strFmt("'%1'进行测试输出, batch has been scheduled.", sParmCaption));

But when I use this piece of code to call RunBaseBatch, there will be no time out error

        batch4democlass1 demo=new batch4democlass1();
        if(demo.prompt())
        {
            demo.run();
        }

I don’t understand the difference between these two.

You shouldn’t use your code. You’re trying to write again what the framework does for you automatically, therefore if even it worked correctly, it would be unncessary (= you wasted time to write it and now you have unncessary code to maintain). But you also introduced some problem there. Just don’t it.

Hello, Martin Dráb
I will now use the code example you gave. There is no problem using it locally. The code has been executed 1000 times in a loop, but when I use the same code in the test environment, the time out error occurs again.
I have now found the reason for the time out error.
The reason for the error seems to be that the token acquisition failed after the loop was executed 100 times.
The error is as follows:

error message “Can read the token but failed validating token with exception 'IDX10205: Issuer validation failed. Issuer: 'https://sts.windows.net/57206206-ec82-4579-9724-0a098ed1b99f/'. Did not match: validationParameters.ValidIssuer: 'null' or validationParameters.ValidIssuers: 'https://wkea-test.sandbox.operations.dynamics.com, 00000015-0000-0000-c000-000000000000, https://wkea-test.sandbox.operations.dynamics.com, https://sts.windows.net/64b7148c-0b9a-41a5-b758-dfd930b73a89/'.'
 'System.IdentityModel.Tokens.Jwt'
    at System.IdentityModel.Tokens.Validators.ValidateIssuer(String issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateToken(String securityToken, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
   at Microsoft.Dynamics.ApplicationPlatform.SystemSecurity.SignedJwtSecurityTokenHandler.ValidateToken(String securityToken, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
 The token validation failed. From confirguration allowed ValidIssuers : 'System.Collections.Generic.List`1[System.String]'
 ValidAudiences : 'System.Linq.Enumerable+<ConcatIterator>d__59`1[System.String]'”

Is the token expired because of the session timeout?

Your question isn’t related to the topic of this thread, Extending RunBaseBatch Class to Create and Run a Batch. I guess it belongs to your other thread, A time out error occurs when running a method on the test environment.

Okay, let’s go back to that thread