Running AX2012 R2 locally on Windows 2012 Server booted directly from VHD

I have been working on a large project since a couple of months now and we use a virtualized environment where every developer has his own proprietary development machine. I am not going to make any statements about virtualization and the hardware that is beneath all of those machines is definitely ok.But here’s the thing:

  • There is a Citrix involved and the network at the customer site does not cope with the traffic now and then.
  • Secondly, the developer machines have enough resources ( or at least they think VMware gives them enough 😉 ) but Dynamics Ax 2012 (R2) is quite the resource consumer (CPU when compiling and memory when generating CIL and performing DB syncs…)

So in the end these machines end up being perceived as too slow and I wanted to see if my personal (physical) machine would perform better when I worked on it locally and just using the network to get to the TFS. The following things I am already sure of:

  • I am not on the company domain which relieves me of all the policies being executed at startup ( Yes, they take forever there! And no I am going to tell you how to run AX 2012 without a domain as that used to be possible in AX2009 but with AX 2012 it is a whole other story, which I am happy to give you some details about if you would ask me apart from this post 🙂 )
  • I am absolutely sure how much resources I own on my machine

The machine on which I am running my test here has the following key specifications: (It is a pretty decent thing (the SSD helps a lot), but you can always argue for more RAM off course 😉

  • HP Elitebook 8570p
  • Intel(R) Core(TM) i5-3320M CPU @ 2.60GHz, 2601 Mhz, 2 Core(s), 4 Logical Processor(s)
  • 8GB DDR3
  • 250 GB SSD drive

There are two goals of this test:

  • Running AX2012 locally and solving my problem of my virtual developer machine being too slow over citrix
  • Booting directly from a VHDX (Hyper-V Virtual Hard Disk) so that I can switch between operating systems and enable full hardware access for my virtual machine. (Except for the disk which is still virtualized because we boot from a VHDX file)

First step : Create a virtual machine and install Windows Server 2012

Now off we go! Off course I am not going into many details of creating a VHD file as this is prerequisite knowledge for this post. It is just creating a virtual machine and installing the Windows Server 2012.

Second step : Creating a boot option for the Windows Server 2012 VHD

The first step was simple, but now comes the interesting part. We know how to create the virtual machine, but how can we boot right of the VHDX instead of having to boot an OS first to be able to run your virtualization software of choice?

Well, BCDEDIT to the rescue! BCDEdit is found in your windows\system32 folder and is used to modify your boot configuration. What we need to do here is to add a boot option that mounts our VHDX file when booting.

Follow the steps in the following link and you will be able to do just that! http://bootdiskette.info/how-to/boot-from-a-vhd/adding-the-vhd-entry-in-boot-menu-and-then-boot-from-vhd/

Third step : Installing a domain controller

AX 2012 cannot run without a domain controller ( captain obvious to the rescue 🙂 ) so we are not going to modify registry keys to trick it into running without one. (As was possible in AX 2009)

Again, there is nice step-by-step guide from Microsoft to install Active Directory Domain Services on your 2012 Server ( Yes, dcpromo has been deprecated 🙂 ) : http://social.technet.microsoft.com/wiki/contents/articles/12370.step-by-step-guide-for-setting-up-windows-server-2012-domain-controller.aspx

If you follow the steps you should have a domain controller on there and your server manager should look similar to the one I have here.

Server manager

Fourth step: Installing SQL Server, Visual Studio and AX2012 R2

I think this step is familiar. You have your own Server and domain controller up an running so you can go ahead and create the needed service accounts and Dynamics AX 2012 R2 has everything it needs to run in there. From here on, it is just like installing your own developer environment.

Conclusion

First thing that I have noticed: Windows Server 2012 really seems to be performing well and handling resources nice. I have the OS running, domain controller active and it only used 1.5 GB of ram (I expected it to use more). The startup time is also acceptable : 2.5 minutes from cold boot to logged in. (Compare that to booting windows 7 in the same time with running all of the company’s policies until I could log in…)

Another advantage here is that I have created a copy of the virtual machine so whenever I run into trouble, I can just boot into the other main OS again and replace the VHD file with a backup and I am good to go again. And when I want to get rid of the dual boot option, I just have to remove the VHDX file and adjust the boot configuration with BCDEdit and it’s gone. All of this minimizes the overhead of having to remaster your machine once in a while because it starts to get slow.

So I hope I showed you how you could do things differently when you want to run Ax locally.

Business Operation Framework and multi-threading

Some of you will be familiar enough with Ax 2009 and therefore know how to create multiple threads when running batch jobs.
For those of you who aren’t : no worries! Since most of it will be done in the same way as before, you will be up to speed in no time.

The main difference in Ax 2012 is the Business Operation Framework that renders the RunBaseBatch framework kinda obsolete. (Read : It’s MS best practice to use BOF instead of RunBaseBatch). The BOF lets you create services and run these services in CIL. I will spare you the full details about CIL as it out of the scope of this article. You can find all the details about creating these services in some very nice posts of a colleague of mine.

Now that you have seen the basics, let’s get to the point of this article. Today we were wondering if the new BOF would also be able to handle multi-threaded batch processing and escpecially how it would be accomplished. Well here’s how…

In this example I will use a rather useless functionality but it’s done like this to keep things simple. I will have a set with some names in it and instead of running a service to display them all we will create two services :

  • The KeSaeBatchService will be used to run the batch job with and devide the work into smaller threads
  • The KeSaeRunTaskService will act as one of those threads running in batch

Creating the run task service

First thing to do is creating the KeSaeRunTaskService and creating a datacontract for it to contain a name it will be passed as a parameter. So start by creating the data contract.

[DataContractAttribute]
public class KeSaeRunTaskDataContract
{
    Name mName;
}

[DataMemberAttribute]
public Name parmName(Name _name = mName)
{
    ;
    mName = _name;
    return mName;
}

Now that we have the contract, let’s create the service class. The service class just contains one method ‘process‘ that will be passed a data contract.

class KeSaeRunTaskService
{
}

[SysEntryPointAttribute]
public void process(KeSaeRunTaskDataContract _theContract)
{
    ;
    // Inside the runTimeTask we just print the name passed
    info(strFmt('%1', _theContract.parmName()));
}

Now create a service within the AOT and add the operation as seen below.

Creating the batch operation service

Now let’s do the same thing all over again, but for the service that will be submitted to the batch framework.
The only additional thing here is to create a menu item to that service to be able to run it.

And last but not least we need to put in some code in the batch service to create smaller runTimeTasks when processing in batch so let’s take a look at the process method.

[SysEntryPointAttribute]
public void process()
{
    Set             theNames = new Set(Types::String);
    SetEnumerator   theEnum;
    Name            theName;
    ;

    // Add some names to the set to process
    theNames.add('Kenny');
    theNames.add('Klaas');
    theNames.add('Kevin');
    theNames.add('Tom');
    theNames.add('Ronald');

    // Create the enumerator
    theEnum = theNames.getEnumerator();

    // Loop all the names
    while (theEnum.moveNext())
    {
        // Get the next name
        theName = theEnum.current();

        // Create a service controller to run the task for processing one name
        mController = new SysOperationServiceController(classStr(KeSaeRunTaskService), methodStr(KeSaeRunTaskService, process));

        // Fetch the data contract from within the controller
        mContract = mController.getDataContractObject('_theContract');

        // Put the current name in the controller's data contract
        mContract.parmName(theName);

        // Check if we are batch processing or not
        if(this.isExecutingInBatch())
        {
            if(!mBatchHeader)
            {
                mBatchHeader = BatchHeader::getCurrentBatchHeader();
            }

            // Create a runTimeTask within the current batch job
            mBatchHeader.addRuntimeTask(mController, this.getCurrentBatchTask().RecId);
        }
        else
        {
            // Just run it immediately
            mController.run();
        }
    }

    // If we're processing in batch, then save the batch header
    if(mBatchHeader)
    {
        mBatchHeader.save();
    }
}

This piece of code differs from Ax 2009 by constructing a SysOperationServiceController instead of a RunBaseBatch class to add as a runTimeTask. This works because the SysOperationServiceController extends the SysOperationController which in it’s turn implements Batchable.

class SysOperationServiceController extends SysOperationController
public abstract class SysOperationController extends Object implements Batchable

That’s about it! Do not forget to compile CIL! Then you should be seeing this when your service is being processed in batch.

And when clicking the parameters button on one of the threads, you can see the name that was passed in the data contract to the thread.

All this can be found in an XPO file available here.

SysOperationFramework : Field display order

Since the arrival of Dynamics Ax 2012, the traditional RunBaseBatch framework is getting obsolete. This has been replaced by the SysOperationFramework. This framework brings a few nice features that were missing in the RunBaseBatch framework. One them is the MVC pattern to separate concerns.

The basics

I would like to start with a few links concerning how to create SysOperationFramework services. A colleague of mine, Klaas Deforce, has created some posts that clearly explain how it works.

All of the post are linked through this overview post : http://www.artofcreation.be/2011/08/21/ax2012-sysoperation-introduction/

Field display order

Well now, let’s come to the question of this post : How can you determine the sequence of dialog fields on the dialogs created by the SysOperationFramework? The answer here lies in the Attributes available also in Dynamics Ax 2012. You have a contract with several datamembers marked with the [DataMemberAttribute] attribute. To determine the sequence you can add the [SysOperationDisplayOrderAttribute] attribute (http://msdn.microsoft.com/en-us/library/gg963068.aspx).

For an example of this, you can check the AssetBalanceReportColumnsContract class, method parmAssetBookId.

[
    DataMemberAttribute('AssetBookId'),
    SysOperationGroupMemberAttribute('Book'),
    SysOperationDisplayOrderAttribute('1')
]
public AssetBookMergeId parmAssetBookId(AssetBookMergeId _assetBookId = assetBookId)
{
    assetBookId = _assetBookId;
    return assetBookId;
}

Groups and group sequence

In the previous sample of code you can also see the SysOperationGroupMemberAttribute attribute. This is to determine which fields belong to a certain group on a dialog. And you can also use a custom sequence on groups by using the SysOperationGroupAttribute attribute as seen in the classDeclaration:

[
    DataContractAttribute,
    SysOperationGroupAttribute('Book', "@SYS95794", '1'),
    SysOperationGroupAttribute('Period', "@SYS40", '2')
]
public class AssetBalanceReportColumnsContract implements SysOperationValidatable
{
    boolean visibleFR;
    AssetBookMergeId assetBookId;
    ToDate closingDatePriorYear;
    ToDate closingDateThisYear;
}

So there you have it. If you want to rearrange things on the dialog, you can use the above method.

 

Propagate infolog messages to the windows event log

The windows event viewer can be a nice tool to check for messages dispatched by the system. You can save the logs in there, reopen them, different kinds of information is available so you can actually trace lots of things in there. But wouldn’t it be nice to also be able to log the messages thrown by Ax 2012 in the windows event log?

That way you do not lose user messages and they are nicely logged into the event viewer. It can also help to log messages received on a client that you cannot seem to reproduce, …

Well, it is possible and here is how to do it in a couple of steps:

  • Add a windows event log and source to put our specific infolog messages in
  • Edit the Ax32 config file to add an event log listener to the configuration

Create event log and source

So first things first, let’s create a windows event log by using the following powershell command

new-eventlog -logname "RealDolmen Ax Solutions" -source "Ax 2012 Infolog"

The result should be like in the figure below

Configure the listener

To add a listener, first open the ax32.exe.config file located in the clientbin directory. You should see a configuration similar to this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0.30319" />
<requiredRuntime version="v4.0.30319" safemode="true"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="EditorComponents"/>
</assemblyBinding>
</runtime>
</configuration>

Modify the configuration so that it looks like this: (It is absolutely important to keep the source name !! The initializeData must be filled with the source you created in the event log)

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="true"/>
<sources>
<source name="Microsoft.Dynamics.Kernel.Client.DiagnosticLog-Infolog" switchValue="Information">
<listeners>
<!-- The initializeData contains the source that was linked to the created event log -->
<add name="EventLog"
type="System.Diagnostics.EventLogTraceListener"
initializeData="Ax 2012 Infolog"/>
</listeners>
</source>
</sources>
</system.diagnostics>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0.30319" />
<requiredRuntime version="v4.0.30319" safemode="true"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="EditorComponents"/>
</assemblyBinding>
</runtime>
</configuration>

Now we are all set up and when firing up the client, any messages to the infolog should be redirected to the event log. So let’s send some error lines to the infolog.

Now check if the same messages appear in the infolog. Normally this is what it should look like:

So there you have it. The messages are nicely logged in the event viewer. As a last remark, you can also adjust the logging level by modifying the switchvalue of the source. Off will not log anything at all, verbose will fill your event log with everything.

Performance in Dynamics Ax 2012

Today I attended what I think was the most interesting session of the conference for me. (There will be one about AIF today but I think this will not as interesting as what I learned about performance)

Basically what they did can be devided in these categories :

  • Scale up and scale out on AOSs and Clients
  • Utilize more SQL features
  • Application effeciency
  • World class diagnosis tools

SysGlobalObjectCache

The first part was about caching. They added a global cache but this time the cache is also available across sessions.
The cache also get syncronised between the AOS instances, but there is a short delay on this, so you never really can trust that the cache is identically on the other AOS Instances.

As to when the objects in the cache are cleared, there are two main triggers for deleting an object from the cache:

  • The limit of number of objects is reached (to be found in the SysGlobalConfiguration table on SQL Server
  • The developer deletes / overwrites the object in the cache

TraceParser

Before the trace parser was a tool to be installed additionally to Ax but now it is part of the system. There are a few ways in which you can use the trace parser:

  • By starting up the performance cockpit from the tools menu
  • By using it to code by starting and stopping trace and specifying a file to log to.

So this time it is actually much more stable and reliable to use. Actually now it is so easy that each developer should develop with the trace parser next to him at all times to keep performance in mind from the beginning.

Data Access : Ad Hoc mode

When your X++ query is getting too big, then use Ad Hoc mode. Here you would actually only select the fields you need.
Also, set the dynamics property on the query fields to false and set OnlyFetchActive to true on the FormDataSource

By selecting only the things you need, the kernel will not join all the tables in the table hierarchy but also the inherited tables where you need fields from.

SQL TempTable

In the AOT, you can now set the TableType on a table object to one of the following :

  • InMemory (The old in memory temp table)
  • Regular (Non temporary table)
  • TempDB (SQL Server tempDB table)

SysOperation

Here I can be brief (because the white paper about this is going to follow later on)

Basically the RunBase framework is now only there for backward compatibility! So now the new SysOperation framework will be used.
This is residing in the AOS also, but more on that when I have a clearer view on this.

So this is what I could capture from the session. Probably missed a lot of other stuff too but for that, we will have the video’s J