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:
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.