Archive

Archive for the ‘Dynamics Ax’ Category

SysOperationFramework : Field display order

December 9th, 2011 No comments

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

November 4th, 2011 No comments

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 client\bin 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.

AX2012 Editor Extensions

November 3rd, 2011 No comments

Today I was reading a very interesting post about possible extensions to the editor in AX 2012 and I would very much like to share it.

Basically it explains that the editor is a hosted visual studio editor (that was already clear) and by knowing this, it is also possible to use the extensions that are available there to the editor inside Ax.
The post I read was dealing with the brace matching extension that is available to do automatic formatting of the braces.

You can read the full post here : http://dev.goshoom.net/en/2011/10/ax2012-editor-extensions/

Client acces log

October 16th, 2011 No comments

I’ve recently read a post on the performance team blog and I found it interesting so I’m posting the link again here.

http://blogs.msdn.com/b/axperf/archive/2011/10/14/client-access-log-dynamics-ax-2012.aspx

The blog post explains how to track user activity within the system.

 

Performance in Dynamics Ax 2012

January 20th, 2011 1 comment

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

IDMF Post Installation Issue : ODBC Connection

December 22nd, 2010 No comments

After the installation of the IDMF there are some post installation steps to do. One of them is to populate the IDMF Management database with data from the Cross Reference.

To do this, some job has to be run on the production database that makes connection to the IDMF database by ODBC. When I tried to do this I get the error that the connection failed. Therefor I have made a little change to the classes responsible for the population.

The DMTPopulateManagementDB\createConnection method is altered to work with a DSN (for some reason it did not work for me if not using a DSN) (And please don’t mind the hard coded name J)

For this to work, we need to create a system DSN on Windows to point to the SQL Server database. So we create one:

But there is one issue here: On a 64-bit 2008 Server, the dynamics client will not find the 64-bit DSN created, so we have to start the 32 bit version of the ODBC Tool.

When the 32 bit DSN is created, it worked for me.

AIF: Service actions lost after recreating service

November 16th, 2010 4 comments

When you delete service objects (classes, macros, service node…) and recreate the service you may have run into the issue that none of the service operations were visible.
Here’s how I solved it today when having the same issue. (We had to remove everything or else the problem was not solved)  

The problem is that the actions are not gone, but they were linked incorrectly. The AIFAction table contains the operations and also contains the classId of the class where the methods to handle the actions are.  

  

Well as the service objects were deleted and afterwards recreated, the class ID of the service class had been altered. Due to this changed ID, none of the operations were visible on the service.  

To fix this, just delete the current records in the AIFAction table for the corresponding service and then we can recreate the actions by calling the AIFServiceGenerationManager class,
method RegisterService.  

AIFServiceGenerationManager::registerService("NVMPPointBalanceService");

Then we can see the operations again in the services form.  

Decisions Dynamics Ax Virtual Conference

November 1st, 2010 No comments

This afternoon the virtual conference will take place at 09:00 am EST (3 pm ET).

Decisions technical conference














The session content can be found here but I’m particularly interested in the Performance session (presented by Dick Wenning) and the Batch framework session (presented by Robert Boone).

Please not that registering can still be done and is completely free of charge !
So see you guys at the virtual conference then ! (That is, if I can get to log in to the conference since I haven’t found the login link yet ;-) )

AIF : Enable net.tcp binding on WCF service

September 14th, 2010 2 comments

Today I wanted to check out what the impact of changing the binding method to net.tcp instead of wsHttpBinding was. But to be able to use net.tcp as a binding, some things need to be done.

IIS Setup

The first thing to do is making sure everything is in place on the IIS 7 side.
For tcp.net being enabled as binding method, make sure the following feature is installed.

By installing this, you can add net.tcp bindings in IIS. So right now we can start by going to the IIS manager and add net.tcp as a binding method on the Dynamics Ax AIF Application.

After adding the site binding, enable the net.tcp binding as protocol on the Application

Dynamics Ax AIF Webservice setup

Once the IIS is ready to pass net.tcp requests, we can configure the Dynamics Ax Web Service to work with net.tcp binding.

Inside Dynamics Ax, go to Basic à Setup à Application Integration Framework à Services and click the configure button. Now the configuration editor will start. Here you can select the service you want to alter and change the binding method to net.tcp. You also have to specify the address here. In this case it is: net.tcp://hostname:808/DynamicsAX50AIFServices/nvmpconfigtableservice.svc

Once you have done this, you can go ahead and regenerate the service artifacts and refresh the services on the calling applications.

Event Handling in X++

September 10th, 2010 1 comment

When you are writing code in C# you can create events and define delegate methods that will be called when the event is triggered. Unfortunately, this is not implemented in the X++ language.

I have read some nice posts about how eventing is implemented in .NET and about the design patterns used (observer, …) and decided to build this myself.

The method I used is using the observer pattern and lets you subscribe to an eventhandler object that can be defined on a class you want to monitor. It is actually quite simple, but can come in handy when available.

Note : Don’t mind the HEL prefix, this is just a prefix used for some tools here. And also, some objects can be extended with additional functionality to contain more information, …

First I created an HELEventHandler class which contains a map with registered listener objects. The methods attachListener and removeListener can be used to register / deregister an object method to listen for the event. The validateListener performs some checks to see if the callback method has the right structure. The fireEvent method is responsible to notify all the listeners of the event triggered.

The HELEventArguments class is created just to pass it when the event is fired. For now, this is an empty shell, but can be extended / adjusted to contain extra information on the fired event.

Now let’s see how we can put this to work. First we start by create a class for testing: HELTestClassEvent. We have a name member here and an eventhandler member.

class HELTestClassEvent
{
    Name mName;
    HELIEventHandler mNameChangedEventHandler;
}

The eventhandler is made public by adding a parameter method.

public HELIEventHandler parmNameChangedEventHandler(HELIEventHandler _nameChangedEventHandler = mNameChangedEventHandler)
{
    ;
    mNameChangedEventHandler = _nameChangedEventHandler;
    return mNameChangedEventHandler;
}

Now let’s say we want an event that is triggered when the name changes. Then we would like to add a method that can be called to notify that the name has changed.

private void onNameChanged(Object _sender, HELEventArguments _arguments)
{
    ;
    if(this.parmNameChangedEventHandler())
    {
        this.parmNameChangedEventHandler().fireEvent(_sender, _arguments);
    }
}

And this will be called like this :

void run()
{
    ;
    mName = "Kenny";
 
    this.onNameChanged(this, new HELEventArguments());
 
    info("I did my work");
}

For now, we have everything we need to let an object notify others that an event has happened. Now we will take a look at how we can register other objects to listen to this event. Let’s create an extra class that will be notified. There, we will first need to create a method that will do the actual work when the event is fired.

public void HELTestClassEvent_OnNameChanged(Object _sender, HELEventArguments _eventArgs)
{
    ;
    info("Who changed the name ?!");
}

And last but not least we actually attach this method to the event by doing this:

void run()
{
    HELTestClassEvent test = HELTestClassEvent::construct();
    ;
 
    // We want to attach a method of this class to the event that the name is changed in the HELTestClassEvent object
    test.parmNameChangedEventHandler(HELEventHandler::construct());
    test.parmNameChangedEventHandler().attachListener(this, methodStr(HELTestClassEventCalledBack, HELTestClassEvent_OnNameChanged));
 
    // Run the HELTestClassEvent object
    test.run();
}

The source can be found here