1

I have a custom AX service operation that can take 5+ minutes to complete and I'm trying to figure out how to abort it from my .NET application, but aborting the client doesn't seem to do anything?

The problem is if I call the operation and the service times out, the AX operation continues on until completion, so I lose visibility to the results (success/failure). An example being a long-running posting operation where I don't know if it posted successfully or not.

I've created a simple demo app where I can't seem to get the operation to abort. In the below code I just create a transaction (ttsbegin/ttscommit), insert into a table at start, sleep, insert into table at end.

Sample AX X++ Service Code:

[SysEntryPointAttribute(true)]
public str callAXTimeDelay(int _sleepSeconds)
{
    Table1                  table1;

    ttsBegin;
    table1.clear();
    table1.SleepData = strFmt("STARTED: %1", DateTimeUtil::utcNow());
    table1.insert();
    ttsCommit;
    
    sleep(_sleepSeconds * 1000);
    
    ttsBegin;
    table1.clear();
    table1.SleepData = strFmt("COMPLETED: %1", DateTimeUtil::utcNow());
    table1.insert();
    ttsCommit;
    
    return strFmt("COMPLETED: %1", DateTimeUtil::utcNow());
}

Then when I call it from .NET, the abort doesn't seem to work? Start/Complete records are still inserted into table1 even though the abort is called before the 15 seconds have completed?

Sample .NET code:

internal class Program
{
    static void Main(string[] args)
    {
        new Program().Run();

        Console.WriteLine("Ended, press any key to exit...");
        Console.ReadKey();
    }

    public void Run()
    {
        AXServicesClient axClient15Sec = new AXServicesClient();
        AXServicesClient axClient5sec = new AXServicesClient();

        var job15sec = DoLongRunningCall(axClient15Sec, 15);
        var job5sec = DoLongRunningCall(axClient5sec, 5);

        try
        {
            var result = Task.Run(() => Task.WhenAny(job15sec, job5sec)).GetAwaiter().GetResult();

            if (result == job15sec)
            {
                Console.WriteLine("job15sec finished first, aborting job5sec");
                axClient5sec.Abort();
            }
            else if (result == job5sec)
            {
                // This code gets executed because the 5 second job completed and
                // it tries to cancel the 15-sec job, but the table ends up with data!
                Console.WriteLine("job5sec finished first, aborting job15sec");
                axClient15Sec.Abort();
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception: " + e.Message);

            axClient15Sec.Abort();
            axClient5sec.Abort();
        }
        axClient15Sec.Close();
        axClient5sec.Close();
    }

    public async Task<string> DoLongRunningCall(AXServicesClient client, int seconds)
    {
        var result = await client.callAXTimeDelay(new CallContext
        {
            Company = "ABCD",
            Language = "en-us"
        }, seconds);

        return result.response;
    }
}
FH-Inway
  • 4,432
  • 1
  • 20
  • 37
William YK
  • 1,025
  • 12
  • 26
  • Could you share the code of `AXServicesClient` and its `Abort` method? In general, a service operation is exposed as a web service call (REST or SOAP). In the most simple case, if you call the service, it triggers the operation in Dynamics without any means to stop it. If you need abort functionality, you need to implement that as additional service operation. A work around for the time out could be an asynchronous implementation where the service calls back to an end point to report when it is finished. – FH-Inway Dec 02 '22 at 16:01
  • I'm not sure the proper terms, but `AXServicesClient` is just the auto generated WCF client when I do `Add Service Reference` in VS and paste in my inbound port WSDL URI (i.e. `http://MYAOS:8101/DynamicsAx/Services/MyCustomService`). – William YK Dec 02 '22 at 16:56
  • The callback idea *from AX* is a clever suggestion! I have 60'ish endpoints that I'd have to rewrite in some fashion to handle that logic, so I'll save that idea for the last resort. My issue is also with standard document services sometimes, due to what I think is some locking by SQL change tracking. I would have to really hack up the standard AIF doc framework to accomplish that, but it's doable as well. Most of my standard doc service requests are **read** operations though so timeouts aren't that big of an issue. It sounds like you're confirming my suspicion that stopping isn't possible. – William YK Dec 02 '22 at 17:01
  • An idea I had, *which feels wrong*, is using the AX `Thread` class to execute the operations. Using the same above test code, I was able to cancel/abort the transaction while in-progress by calling `\System Documentation\Classes\Thread\cancelJob`. There's not a lot of information around the `Thread` class and from where I can find it used in `AX` it seems only client-side operations? Is that a terrible idea? – William YK Dec 02 '22 at 17:06
  • I added some more relevant tags, hopefully someone more knowledgeable in them can answer your question. In the mean time, consider adding the information you gave in the comments to the question. This makes it easier for others to understand your question and increases your chances of receiving a helpful answer. – FH-Inway Dec 04 '22 at 10:57

0 Answers0