.NET Interop to X++ When X++ Runs as CIL

In Microsoft Dynamics AX 2012 we have interoperability features that enable your .NET Framework C# code to call methods on AX classes and tables. We call this feature .NET interop to X++ .

In AX 2012 we also added the ability to run X++ code as .NET CIL. Most X++ as CIL scenarios run as services or batch jobs. For more information related to X++ code running in .NET CIL, see AX help topic on MSDN titled X++ Compiled to .NET CIL.

This blog post explains the issues that can arise when these two features interact.  And I show you the proper techniques for achieving error free interop when this interaction might occur.

Proxies

Before the interop scenarios can begin, you must use Visual Studio to automatically generate a proxy class. The proxy class represents an AX X++ class to your C# code.  Proxy classes have the following characteristics:

  1. Proxy classes are generated as C# or Visual Basic source code. Normally the proxies are generated when a class or table is dragged from the Application explorer to the managed solution.
  2. Proxies can be created for the following AX AOT items: classes, tables, and enumerations.
  3. Each proxy provides a type safe and natural interface to its corresponding AOT item.
  4. When your C# code constructs an instance of a proxy, the system simultaneously constructs an instance of the corresponding class inside the AX system.
  5. A proxy class has the same method names as the X++ class that the proxy represents.
  6. Proxies are compiled along with the rest of your Visual Studio project.
  7. The C# code inside a proxy makes calls to the assembly Microsoft.Dynamics.AX.ManagedInterop.dll.
    We speak of this assembly as the managed interop layer, or MIL. MIL has some similarities to .NET Business Connector.   MIL uses the late-bound approach to attach and run the methods on the X++ class or object. The late binding is hidden under the covers: your C# code uses the proxy class in the standard early-bound approach.

For more information related to proxies, see our AX Help topic on MSDN titled Proxy Classes for .NET Interop to X++ .

 Two Scenarios

The standard scenarios begin with a call from X++ to your .NET assembly.  Then in your C# code you construct an instance of a proxy class, and call methods on the proxy instance.

Scenario #1: ExecuteStmt method on an AX Table

In this scenario your C# program constructs an instance of a proxy to an AX table.  Of course the C# proxy class for the table must have already been generated and compiled.

Your C# code calls the ExecuteStmt method on the proxy instance, and obtains data from the table. The ExecuteStmt takes a string of SQL that is compiled at run time.

This scenario works well when the X++ code runs in interpreted mode.

X++ code to call your static C# method:

    str returnValue = TestClassLibrary.TestClass::TestExecuteFunction(); // X++

C# code constructs a proxy instance, and executes a query from TestTable:

   namespace TestClassLibrary // C#
    {
        public class TestClass
        {
            public static string TestExecuteFunction()
            {
                TestTable testTable = new TestTable(); // TestTable proxy
                testTable.ExecuteStmt("select firstonly * from %1");
                return testTable.Name;
            }
        }
    }

 

Issue:   If the call from the proxy to the X++ method ExecuteStmt runs as CIL, a Microsoft.Dynamics.AX.ManagedInterop.OperationNotSupportedInILException exception is thrown.

Solution:   To avoid this issue, switch from calling ExecuteStmt with a string of X++ SQL, to calling a X++ method that contains direct X++ SQL statements.  Further, your C# code can ascertain whether the X++ is being interpreted or is running as CIL. The MIL Session.IsILSession method ascertains whether the X++ code is running as CIL. After calling IsILSession, your C# code can branch to ExecuteStmt or direct X++ SELECT statements as appropriate.

For the direct X++ SQL statement approach, you can create an X++ HelperClass class which contains a method. The method should query the AX TestTable directly by using the SELECT statement of X++. The SELECT statement is compiled at compile time.

Have your C# code construct an instance of a proxy for the HelperClass. Then your C# code can call the method on the proxy instance.

The following code example has a modified version of the C# method TestExecuteFunction.  The modified C# code uses the option of calling the MIL IsILSession method.

  using MIL = Microsoft.Dynamics.AX.ManagedInterop; // C#
    namespace TestClassLibrary
    {
        public class TestClass
        {

            public static string TestExecuteFunction()
            {
                TestClassLibrary.TestTable testable; // TestTable is proxy of X++ table
                if (MIL.Session.IsILSession()) // X++ running as CIL
                {
                    // Calls an X++ static method on helper class
                    // to execute X++ code to fetch first record from TestTable.
                    testTable = HelperClass.getTestTableFirstonly();
                }
                else // X++ is interpreted
                {
                    testTable.ExecuteStmt("select firstonly * from %1");
                }
                return testTable.Name;
            }
        }
    }

An alternative coding strategy would be to always code and call a HelperClass, so you would have no need to call the IsILSession method.

Here is the method on the X++ class HelperClass:

    public static TestTable getTestTableFirstonly() // X++
    {
        TestTable testTable;
        select firstOnly * from testTable;
        return testTable;
    }

 Note that writing the data access code in this early-bound approach in X++ provides a better experience than writing a late-bound text string in a managed source code file. The X++ editor provides IntelliSense, and the X++ compiler complains about errors early at compile time, not later at runtime.

 

Scenario #2: MIL Methods that Create X++ Classes and Tables

In this scenario your C# program constructs an instance of an AX X++ class and an AX table, by using MIL directly.  This approach works when the X++ code is run by the AX interpreter.

X++ code to start the scenario:

    TestClassLibrary.TestClass::TestSessionCurrentFunction(); // X++

The called C# code:

In this C# code, MIL is used to obtain an instance of the MIL Object class, for the X++ class named XppClassName.  MIL is also used to obtain an instance of the MIL Record class, for the AX table named XppTableName.  Then static methods are called on the X++ class and the X++ table.

    using MIL = Microsoft.Dynamics.AX.ManagedInterop; // C#
    namespace TestClassLibrary
    {
        public class TestClass
        {
            public static void TestSessionCurrentFunction()
            {
                string sXppClassName = “XppClassName”;
                string sXppTableName = “XppTableName”;
                MIL.Session.Current.CreateObject(sXppClassName);
                MIL.Session.Current.CreateRecord(sXppTableName);
                MIL.Session.Current.CallStaticClassMethod
                    (sXppClassName, "XppClassMethod");

                MIL.Session.Current.CallStaticRecordMethod
                    (sXppTableName, "XppTableMethod");
            }
        }
    }

Issue:   If the X++ code that initiates the scenario is running as CIL, the code example would suffer a System.NullReferenceException exception when references are made to the MIL.Session.Current object (which is null in this case).

Solution:   Always use the proxy classes instead of using the MIL Session class methods. This is shown below:

    namespace TestClassLibrary // C#
    {
        public class TestClass
        {
            public static string TestSessionCureentFunction()
            {
                // XppTableName is a proxy class to an AX AOT table.
                XppTableName xppTableInstance = new XppTableName();

                // XppClassName is a proxy class to an AX AOT X++ class.
                XppClassName xppClassInstance = new XppClassName();

                XppClassName::XppClassMethod();

                XppTableName::XppTableMethod();

            }
        }
    }

Note:   The Axapta class of the Business Connector .NET is roughly analogous to the MIL Session class.  Use of the Axapta class would have the same CIL-related issues as we discussed for the MIL Session class.