Tuesday, August 20, 2013

XLNT - A Most "Excellent" Framework for X++

Although demonstrated and praised quite a few times by Master Obi-Wan Villadsen and his team, the XLNT framework is still a little-known framework that contains a lot of power. So much so that the static code analysis (aka "Customization Analysis") in the Dynamics Lifecycle Services almost solely based on the XLNT framework.

So what is the XLNT framework exactly? XLNT basically allows us to hook into the X++ compiler to get the parsed code and run analysis and diagnostics.
How do you get XLNT? There's no XLNT download in itself, but the XLNT framework is used for the Code Upgrade Service. You can download the Code Upgrade Service from InformationSource (you need to be a Dynamics Customer or Partner) and browse to the SERVICES page. Or you can just click this link. When you've download the package, you can just open the EXEcutable with your favorite compression utility (or you can run the installer) and the only files you really need for this article are:

Microsoft.Dynamics.AX.Framework.Xlnt.XppParser.dll
Microsoft.Dynamics.AX.Framework.Xlnt.XppParser.Pass2.dll

Feel free to play with the rest, but it requires models to be installed etc to do the code upgrade service. So for the purpose of what we're talking here, those two DLLs are all we need.

So, to make both the coding and this article a bit easier, I'm going to stay entirely in Visual Studio. XLNT does need the AX "context" so if we won't be running this in AX but entirely in .NET from Visual Studio, we'll use a business connector session to provide the context we need. I love XAML as a UI for test apps, but considering this is a blog and not everyone is familiar with XAML, I will just use a console app so avoid distracting from what we're trying to do with XLNT. Once you create your project, right-click on the project node in the solution explorer and select properties.


Next, we need to change the Target framework to .NET Framework 4 (default is client profile 4.0).


Finally, the AX assemblies are built against .NET Framework 2.0. If you would try to run it as-is, you'll get an error saying that "Mixed mode assembly is built against version v2.0.50727 of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information". The "additional configuration information" has to happen in the app.config of your console app. If you're making a class library for use inside of the AX process, this shouldn't be an issue at all. But in a standalone app like this console app, open the app.config and add the attribute useLegacyV2RuntimeActivationPolicy="true" to the startup node, so your app.config xml file should look like this:

<?xml version="1.0"?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>


Let's get down to it. I'm working in VS 2010 for this example. This would allow me to add any code to the AOT later if I wanted to use this code from within AX. Create a new C# console application, I'm calling it XLNTConsoleApp. In the solution explorer, right-click references and first add references to the XLNT dlls mentioned before. It doesn't matter where you have them stored, Visual Studio will copy them into your solution by default.


Additionally, we'll need to reference the business connector so that we can provide the AX session context needed for XLNT to work. Assuming you have the business connector installed (if not, please do so :-)) you can find the DLLs in your client/bin folder (default is C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin). The files you need are:
Microsoft.Dynamics.AX.ManagedInterop.dll
Microsoft.Dynamics.BusinessConnectorNet.dll


After that, we're ready to write some code. First, we'll load up a connection to AX using the business connector. Alternatively to all of this, you could just create a class library project, add it to the AOT, and then call the code from an X++ job or so. So, to account for both, let's try and detect if we have an AX session context already (if we're running from within AX) and if not, we'll create one (uses BC.Net). In your Program.cs locate the static Main method and add the following code, which looks for a runtime context and checks if you're logged in, if not it makes a BC connection. Of course, we do some basic exception handling.

static void Main(string[] args)
{
    Microsoft.Dynamics.BusinessConnectorNet.Axapta ax = null;

    try
    {
        // Check for a RunTimeContext.Current, which will exist if we're running this
        // from within the AX process
        if (Microsoft.Dynamics.AX.ManagedInterop.RuntimeContext.Current == null
            || Microsoft.Dynamics.AX.ManagedInterop.RuntimeContext.Current.isLoggedOn() == false)
        {
            ax = new Microsoft.Dynamics.BusinessConnectorNet.Axapta();
            // Supply all nulls to use the Client Configuration settings for Business Connector
            ax.Logon(null, null, null, null);
            // alternatively, use the line below to specify a config file
            //ax.Logon(null, null, null, @"C:\path_to_my_config\configfile.axc");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message); // output the exception message
        Console.ReadLine(); // pause execution before we quit the console app
        return;
    }

    try
    {
        // [CODE WILL GO HERE]
        Program.BasicTest("void test() { if (true) { info('hello, world'); } }");
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message); // output the exception message
    }

    if (ax != null)
    {
        ax.Logoff();
        ax.Dispose();
    }

    Console.ReadLine();
}


Note the line that says // [CODE WILL GO HERE]. That is where we will be introducing our experiments with XLNT. For now, let's create one basic method for you to experiment with until the next article :-)

static void BasicTest(string sourceCode)
{
    ProxyMetadataProvider metaData = new Microsoft.Dynamics.AX.Framework.Xlnt.XppParser.Pass2.ProxyMetadataProvider();
    MultipassAdministrator multipassAdmin = new MultipassAdministrator(metaData);

    Method method = multipassAdmin.CompileSingleMethod(sourceCode) as Method;

    if (method != null)
    {
        foreach (Statement statement in method.Statements)
        {
            Console.WriteLine(statement.ToString());
        }
    }
}


This will create a metadata provider and a "multipass administrator" which basically let's you compile things. We'll give it some source code to compile which we pass in as a string. Note that you can point it to AOT objects etc. Finally, we'll loop over the statements in the method. Note that statements can be nested. The Statement class is a base class for different types of statements, which will all have different properties for sub-statements etc (eg the if statement ("IfStmt" class) has an expression statement and a "consequent" property).

You can explore the statements by adjusting the sourceCode input and putting a breakpoint inside the foreach loop, for example.
Of course, you still need to actually call this new method, so in the [CODE WILL GO HERE] section you can put the following:

Program.BasicTest("void test() { if (true) { info('hello, world'); } }");


Note that a compile error will not result in an exception but rather it will just return null instead of an actual method instance.

Alright, now you're ready to do something interesting with XLNT! We'll explore more in the next article, but if you're doing some exploratory work of your own, please post in the comments!

4 comments:

  1. Awesome information. I look forward to the next article.

    ReplyDelete
  2. Interesting...I hope it does not open door for someone to run unwanted code in AX.

    ReplyDelete
    Replies
    1. Well, I haven't checked what kind of access you need to run this, but I assume you need developer access. Secondly, this does not allow you to RUN code, just compile it and get analytics.

      Delete
  3. This is such a great resource that you are providing and you give it away for free. I love seeing websites that understand the value of providing a quality resource for free. It?s the old what goes around comes around routine. Did you acquired lots of links and I see lots of trackbacks??
    Dynamics crm training

    ReplyDelete