Resolved C# wrapper Async method not executing AX 2012 method but sync is working

Dhruvil

Member
Joined
Dec 19, 2020
Messages
12
Programming Experience
3-5
I am trying to call my AX 2012 method async using C# wrapper but the AX 2012 method is not executing. The same data is correctly processed by sync call and is executing properly. Below is the code for Controller in C#. Why is it not executing async method ?

I have tried to call it without await as well as await. I tried debugging but it wont probably work due to different thread.

The sync part is working properly but the MS Flow calling this API times out due to limitations in Flow.

Can you please tell me how to make the async work?


C#:
//It dynamically sets the AIF Url and calls the method to consume AIF services
using SXA_FlowIntegrationWebAPI.Helpers;
using SXA_FlowIntegrationWebAPI.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace SXA_FlowIntegrationWebAPI.Controllers
{
public class MainController : ApiController
{
    [ActionName("Method")]
    [HttpPost]
    [Authorize]
    public IHttpActionResult CallMethod(ClassMethodCallRequest request)
    {
        dynamic retValue;
        using (var client = new AX2012.SXAFlowIntegrationServiceClient())
        {
            try
            {
                var callContext = new AX2012.CallContext();
                callContext.Company = request.companyId;
                callContext.LogonAsUser = User.Identity.Name;

                client.InnerChannel.OperationTimeout = new TimeSpan(0, 10, 00);
                client.Endpoint.Address = new System.ServiceModel.EndpointAddress(request.aifURL);
                if (request.methodName == "async")
                {
                    retValue = "";
                    //client.callMethodAsync(callContext, request.className, request.methodName, request.dataJsonStr);
                    CallMethodAsyncCustom(client, callContext, request);
                }
                else
                {
                    retValue = client.callMethod(callContext, request.className, request.methodName, request.dataJsonStr);
                }
            }
            catch(Exception e)
            {
                return Json(new ClassMethodCallResponse
                {
                    status = "Error",
                    userName = User.Identity.Name,
                    className = request.className,
                    methodName = request.methodName,
                    aifURL = request.aifURL,
                    error = e
                });
            }
        }
        return Json(new ClassMethodCallResponse
        {
            status = "Success",
            userName = User.Identity.Name,
            className = request.className,
            methodName = request.methodName,
            aifURL = request.aifURL,
            data = retValue,
        });
    }

    public static async System.Threading.Tasks.Task<string> CallMethodAsyncCustom(AX2012.SXAFlowIntegrationServiceClient client, AX2012.CallContext callContext, ClassMethodCallRequest request)
    {
         await  System.Threading.Tasks.Task.Run(() => client.callMethodAsync(callContext, request.className, request.methodName, request.dataJsonStr));
         return "Completed";
    }
}
 
Solution
I resolved the error by using client.Open() and client.close() instead of creating client inside of using keyword. I guess after async call return, it was trying to access the object which was already disposed.

But my main issue remains unsolved. The JSON is not returned immediately back to Power Automate. It is working just like sync. Then I used await task.run (() => client.callAsync (....)) , it also waited and didn't get response immediately. But even after my flow timed out. It performed my required task. But here the issue was it was doing it twice.

My task is to insert lines in a table. It created header twice. And inserted lines in 1st header until timed out. And then again inserted lines creating 2nd header. In this all...
Cross posted in StackOverflow:

And in the CRM Dynamics Site (which by the way is the appropriate place for this question since this seems to be a question that requires specific knowledge about CRM Dynamics APIs):
 
And you'll take care of telling the other forums that a solution has been found when one does come up so that the other people will stop wasting their time?

Anyway, where is the documentation for AX2012.SXAFlowIntegrationServiceClient.callMethodAsync()? Does it truly call thing asynchronously as the name implies? My Google searches for it has only turned up your questions, but no documentation for it.

Anyway, if AX2012.SXAFlowIntegrationServiceClient.callMethodAsync() were truly following the modern naming convention where *Async() implies that it returns a Task, then the issue looks to be in these lines of code:
C#:
public static async System.Threading.Tasks.Task<string> CallMethodAsyncCustom(AX2012.SXAFlowIntegrationServiceClient client, AX2012.CallContext callContext, ClassMethodCallRequest request)
{
     await  System.Threading.Tasks.Task.Run(() => client.callMethodAsync(callContext, request.className, request.methodName, request.dataJsonStr));
     return "Completed";
}

This is because the await there is awaiting the lambda, but the lambda just goes and calls another asynchronous method without awaiting. You should just have written that method as:
C#:
public static async System.Threading.Tasks.Task<string> CallMethodAsyncCustom(AX2012.SXAFlowIntegrationServiceClient client, AX2012.CallContext callContext, ClassMethodCallRequest request)
{
     await  client.callMethodAsync(callContext, request.className, request.methodName, request.dataJsonStr);
     return "Completed";
}
 
Hi,
Sorry for posting in multiple forums. I assure you I will update it in all once I get the solution.

As for the callMethodAsync document, here is the code,
C#:
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
        SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodResponse SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationService.callMethod(SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodRequest request) {
            return base.Channel.callMethod(request);
        }
        
        public string callMethod(SXA_FlowIntegrationWebAPI.AX2012.CallContext CallContext, string _className, string _methodName, string _data) {
            SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodRequest inValue = new SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodRequest();
            inValue.CallContext = CallContext;
            inValue._className = _className;
            inValue._methodName = _methodName;
            inValue._data = _data;
            SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodResponse retVal = ((SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationService)(this)).callMethod(inValue);
            return retVal.response;
        }
        
        [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
        System.Threading.Tasks.Task<SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodResponse> SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationService.callMethodAsync(SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodRequest request) {
            return base.Channel.callMethodAsync(request);
        }
        
        public System.Threading.Tasks.Task<SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodResponse> callMethodAsync(SXA_FlowIntegrationWebAPI.AX2012.CallContext CallContext, string _className, string _methodName, string _data) {
            SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodRequest inValue = new SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodRequest();
            inValue.CallContext = CallContext;
            inValue._className = _className;
            inValue._methodName = _methodName;
            inValue._data = _data;
            return ((SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationService)(this)).callMethodAsync(inValue);
        }


I tried using your suggested code, but it is still not working.
 
Set a breakpoint and trace into the calls.
 
Also your line 34 in your original post is not awaiting on the task that gets returned.
 
I don't actually need the reply from async method. I have handled any errors in AX 2012 side for that. But the main issue is the AX 2012 method is not getting called.

Also, I wanted to ask that is the async performed on C# side ? Or do I have to manually write the code for async in AX 2012?

This is the interface code :
C#:
public interface SXAFlowIntegrationService {
       
        // CODEGEN: Generating message contract since the wrapper name (SXAFlowIntegrationServiceCallMethodRequest) of message SXAFlowIntegrationServiceCallMethodRequest does not match the default value (callMethod)
        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/SXAFlowIntegrationService/callMethod", ReplyAction="http://tempuri.org/SXAFlowIntegrationService/callMethodResponse")]
        [System.ServiceModel.FaultContractAttribute(typeof(SXA_FlowIntegrationWebAPI.AX2012.AifFault), Action="http://tempuri.org/SXAFlowIntegrationService/callMethodAifFaultFault", Name="AifFault", Namespace="http://schemas.microsoft.com/dynamics/2008/01/documents/Fault")]
        SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodResponse callMethod(SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodRequest request);
       
        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/SXAFlowIntegrationService/callMethod", ReplyAction="http://tempuri.org/SXAFlowIntegrationService/callMethodResponse")]
        System.Threading.Tasks.Task<SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodResponse> callMethodAsync(SXA_FlowIntegrationWebAPI.AX2012.SXAFlowIntegrationServiceCallMethodRequest request);
    }

I have only method "callMethod" in Ax 2012. Can it be possible that it is searching for "callMethodAsync" in Ax 2012 ?

I am assuming that it should call my method "callMethod" in Async manner instead of "callMethodAsync".

Edit: Also that the above interface code is auto generated .
 
Even if you don't need the reply, you still need to await it. async/await does not magically create a thread to run code. Code needs to run on some thread, so by using await you are essentially yielding control of the current thread over to the code that is encapsulated by the Task.
 
Also, I wanted to ask that is the async performed on C# side ? Or do I have to manually write the code for async in AX 2012?
I have no idea, and don't particularly care. Microsoft's aquisition Great Plains to eventually make it CRM Dynamics is indirectly the reason why left Microsoft the first time. To say that I hold a grudge is an understatement.

What does the documentation say?
 
Last edited:
I tried to debug it and got these exceptions when my async call is made from "CallMethodAsyncCustom" method. I had made that method too return type void instead of string so that I can call from my original method without any warnings.

C#:
A first chance exception of type 'System.ServiceModel.CommunicationObjectAbortedException' occurred in System.ServiceModel.dll
A first chance exception of type 'System.ServiceModel.CommunicationObjectAbortedException' occurred in System.ServiceModel.Internals.dll
A first chance exception of type 'System.ServiceModel.CommunicationObjectAbortedException' occurred in System.ServiceModel.Internals.dll
A first chance exception of type 'System.ServiceModel.CommunicationObjectAbortedException' occurred in System.ServiceModel.Internals.dll
A first chance exception of type 'System.ServiceModel.CommunicationObjectAbortedException' occurred in System.ServiceModel.Internals.dll
A first chance exception of type 'System.ServiceModel.CommunicationObjectAbortedException' occurred in System.ServiceModel.Internals.dll
A first chance exception of type 'System.ServiceModel.CommunicationObjectAbortedException' occurred in mscorlib.dll
A first chance exception of type 'System.ServiceModel.CommunicationObjectAbortedException' occurred in mscorlib.dll

Do you know why these exceptions would have been generated?
 
What does the documentation say?
 
I resolved the error by using client.Open() and client.close() instead of creating client inside of using keyword. I guess after async call return, it was trying to access the object which was already disposed.

But my main issue remains unsolved. The JSON is not returned immediately back to Power Automate. It is working just like sync. Then I used await task.run (() => client.callAsync (....)) , it also waited and didn't get response immediately. But even after my flow timed out. It performed my required task. But here the issue was it was doing it twice.

My task is to insert lines in a table. It created header twice. And inserted lines in 1st header until timed out. And then again inserted lines creating 2nd header. In this all lines were inserted as required.

How to return my response first and then start this execution of method ? And why is the task running twice once till my flow is timed out and again creating all lines correctly?
 
Solution
But my main issue remains unsolved.

I beg to differ. from post #1:
I am trying to call my AX 2012 method async using C# wrapper but the AX 2012 method is not executing.

From post #13 it sounds that you solved your problem and the method is now executing.

Open a new thread if you feel that your asynchronous method is not executing asynchronously.
 
Back
Top Bottom