Skip to content

Compile backwards

Today I noticed that Type hierarchy browser in AX 2012 R3 allows you to compile types backwards:

HierarchyBrowserCompile

Context menu in AOT supports only forward compilation.

It’s possible that the feature is there for some time and it just took me long time to notice it, because I normally don’t compile code from Type hierarchy browser.

Stakeholder license for Visual Studio Online

Brian Harry announced some upcoming changes in licensing of Visual Studio Online (and later for Team Foundation Server too). Everybody should be delighted by the new Stakeholder license, which will be completely free. It will have following permissions:

  • Full read/write/create on all work items
  • Create, run and save (to “My Queries”) work item queries
  • View project and team home pages
  • Access to the backlog, including add and update (but no ability to reprioritize the work)
  • Ability to receive work item alerts

That’s a lot of things that people can do without any cost, isn’t it? Now everybody will be able to participate in a VS Online / TFS project without any special licensing requirements.

Issue Search

Issue Search is obviously not the coolest part of Microsoft Dynamics Lifecycle Services, but it’s very useful and I would like to show you a few tricks.

If you’ve never heard about Issues Search before, it’s a relatively simple web page where you can find hot fixes released for AX 2012 and even issues currently being investigated. This is how the main page looks like:SearchResults

You can see that there is a text box for search input, some filters and results. Notice that you can filter by version of AX (you sometimes need to open Advanced search and adjust this setting to get what you want), that different states of issues have different colors and that you can directly see KB number and the release date (if applicable).

If you click on an issue, you’ll see a bit more details and most importantly you’ll be able to download released hotfixes.

IssueDetails

Often you can even see changes done by Microsoft in X++ code, which allows you to quickly review the solution and impact on your application (by using View changes or clicking on a particular affected object).

CodeChanges

If you’re familiar with Team Foundation Server, you probably know that this web-based code comparison is a standard feature, so it’s not something unique to Dynamics Lifecycle Services. But it’s unusual to see such a level of transparency from software vendors and I must appreciate that Microsoft decided to share all these details with us.

Now if you return to the main page, you can see the following suggestion in the input box: Enter a keyword, KB number or AOT object path ($\ObjectType\Object or $\ObjectType\Object#element, e.g. $\Classes\Tax#post). If you suspect that something is wrong in a specific object or method, you can easily find all related fixes:

preRunModifyContract

I also noticed that in AX 2012 R3 you can open Issue Search for a given object directly from AOT.

AOTContextMenu

Just note that this currently works only for root elements, not for individual methods.

I never enjoyed searching for hotfixes on PartnerSource; Issue Search is so much better. I don’t say it’s perfect, but it’s evolving, together with the rest of Dynamics Lifecycle Services. And you can help it to evolve by sending some feedback to the product team – just use the smiling icon in the top right corner. Smile

I can tell you from my experience that they don’t ignore feedback (which, of course, doesn’t necessarily mean that your favorite feature will get on the top of their backlog).

Index fragmentation on xRef tables

Building cross-references for code in Dynamics AX creates millions of records in xRef tables. It has also very negative impact on fragmentation of database indexes.

This is an example of indexes on xRefReferences table from a customer’s environment after rebuilding all cross references.

Before

And here the same set of indexes a few minutes later after a rebuilding indexes:

After

If an index is heavily fragmented, it’s much slower to scan it and you’ll notice performance degradation of all queries using the index. It’s not unusual that opening Add-ins in the context menu in AOT takes several seconds just because of a simple check to xRef tables (to see whether cross-references exist for the given object). And because the index can’t be used effectively, of course.

It’s should be obvious that having right indexes isn’t sufficient – they also have to be in a good shape.

It’s wise to have a scheduled job to maintain fragmentation and statistics of all indexes, nevertheless you may also want to rebuild indexes for xRef tables immediately after updating cross references.

Instrumentation and tracing

Regardless of how hard we try to write flawless software, sooner or later something goes wrong and we must identify where exactly the problem lies (to be able to fix it). One option is using the debugger, but it’s not always possible – we can’t reproduce the problem, for example. Another way is inserting some trace messages into code (= instrumenting the code) and logging them somewhere – it can reveal what code was called, values of certain variables, identity of a user executing some method and so on.

There are quite a few things you may want to do with such trace messages – filter them by priority or origin, save them to various destinations (e.g. text/XML/EventLog), activate/deactivate tracing without rebuilding your application and so on.

The good news is that .NET already contains such a framework and we can use it.

TraceSource in .NET (4.0)

Instead of jumping into details, let’s look at a rather simple example. If you find it interesting, you can learn more in Tracing and Instrumenting Applications.

We’ll need two main components:

  • A trace source. We’ll pass information about interesting events to the source, but the source itself doesn’t know how (or whether) messages will be logged.
  • Trace listeners. Listeners get information from a trace source and log them somewhere.

In your source code, you simply create a named TraceSource instance and use one of its methods to record an event:

System.Diagnostics.TraceSource ts = new System.Diagnostics.TraceSource("Goshoom.DemoApplication");
ts.TraceInformation("Started");

Because we don’t want to create a new TraceSource every time, it’s typically stored in a static field.

Even if we write something into a trace source, nothing is going to happen unless there is a listener. Listeners can be registered in code as well, but the usual approach is using configuration files, because it’s much more flexible. Check out the following fragment of a configuration file:

<system.diagnostics>
  <sources>
    <source name="Goshoom.DemoApplication" switchValue="Information">
      <listeners>
        <add name="TextFile" 
             type="System.Diagnostics.TextWriterTraceListener" 
             initializeData="DemoApplication.log" />
      </listeners>
    </source>
  </sources>
</system.diagnostics>

We identify the trace source by its name (that’s the value we passed to TraceSource’s constructor) and set a switch saying that we’re interested in messages of severity “Information” and higher. It gives you great flexibility – you can, say, log only errors in production environments and switch to more details only when needed and only for the trace source related to the failing component.

Then we add one listener which writes messages received from the trace source to a text file. You can have several listeners for a single source, listeners shared between sources etc. See Trace Listeners on MSDN to learn a bit more about various types of listeners.

Dynamics AX 2012

If you create .NET libraries to be used by AX as part of an AX process itself, the approach is exactly the same – you’ll create and use a trace source in your library and configure listeners in Ax32.exe.config (if the library is used by AX client) or Ax32Serv.exe.config (for AOS). Naturally, you can also listen to events recorded by existing libraries, such as WCF. (It sometimes helps me a lot, just note that you can’t configure diagnostics in endpoint configuration, you have to do that in AOS configuration).

Furthermore, you can use exactly the same approach in X++ via .NET Interop – our example mentioned before as C# is also valid X++ code:

System.Diagnostics.TraceSource ts = new System.Diagnostics.TraceSource("Goshoom.DemoApplication");
ts.TraceInformation("Started");

If X++ code doesn’t run in CIL, there is a performance penalty when invoking calls via .NET Interop. It’s nothing you have to care about if you do few calls, but it may cause troubles if it’s used in huge loops, for instance.

Infolog

Infolog messages created in AX are also automatically passed to a trace source and you can use usual techniques to log them. If you look at the MSDN article about propagating infolog messages to Event Log, you will recognize the pattern described before:

<configuration>
  <system.diagnostics>
    <trace autoflush="true"/>
    <sources>
      <source name="Microsoft.Dynamics.Kernel.Client.DiagnosticLog-Infolog"
              switchValue="Information">
        <listeners>
          <add name="EventLog" 
               type="System.Diagnostics.EventLogTraceListener" 
               initializeData="Dynamics Infolog"/>
          <add name="TextFile" 
               type="System.Diagnostics.TextWriterTraceListener" 
               initializeData="DynamicsInfologTrace.log" 
               traceOutputOptions="DateTime"/>
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
</configuration>

We have two listeners there (writing messages to Event Log and to a text file) for messages received from Microsoft.Dynamics.Kernel.Client.DiagnosticLog-Infolog trace source.

Let’s try another type of listener – XmlWriterTraceListener:

<add initializeData="infolog.svclog" type="System.Diagnostics.XmlWriterTraceListener"
    name="xmlListener" traceOutputOptions="DateTime, Callstack" />

You can open such files in any text or XML editor, but there is one more option – Service Trace Viewer. I intentionally used .svclog extension instead of .xml, because .svclog files are associated with Service Trace Viewer. If you don’t have it installed, you can get it as part of Windows SDK. See how it looks like:

Service Viewer - Infolog

It has a lot of great features regarding visualization, filtering, opening several trace files in the same time (e.g. client and AOS or multiple AOSes) and so on. You may not appreciate that too much when working with infolog messages, but it’s extremely useful when things start to be a bit more complicated.

I selected Callstack as one of output options – let’s look at what we get from AX:

at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at System.Diagnostics.TraceEventCache.get_Callstack()
at System.Diagnostics.XmlWriterTraceListener.WriteFooter(TraceEventCache eventCache)
at System.Diagnostics.XmlWriterTraceListener.TraceEvent(TraceEventCache eventCache, String source, TraceEventType eventType, Int32 id, String message)
at System.Diagnostics.TraceSource.TraceEvent(TraceEventType eventType, Int32 id, String message)
at Microsoft.Dynamics.Kernel.Client.DiagnosticLog.AddInfologMessage(TraceEventType eventType, Int32 errorType, String message)
at Microsoft.Dynamics.Kernel.Client.DiagnosticLogWrapper.AddInfologMessage(DiagnosticLogWrapper* , Int32 errorType, Char* message)
at System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)
at Microsoft.Dynamics.Kernel.Client.NativeWindowWithEvents.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at _wWinMainCRTStartup()

If you expected an X++ call stack, this must be a huge disappointment. All what we get is a sequence of .NET architecture calls that as some point call X++ (CallWindowProc()). Although it makes a good sense, it renders the log much less useful than it usually would be. Normally we want to know where exactly an event occured.

We can’t change the call stack generated by .NET – it’s even correct, it’s just not what we want. But we can make a workaround – we can create an additional trace message containing the X++ call stack. We’ll make following changes to the Info class:

classDeclaration
{
    System.Diagnostics.TraceSource ts;
    …
}
 
new
{super();
    ts = new System.Diagnostics.TraceSource("Microsoft.Dynamics.Kernel.Client.DiagnosticLog-Infolog");
    …
}
 
private void addTraceCallstack(str _txt)
{
    str msg = _txt + " - Callstack";
    container c = xSession::xppCallStack();
    str path;
    int i;
    boolean found;
    #AOT
 
    for (i = 1; i <= conLen(c); i += 2)
    {
        path = conPeek(c, i);
        path = substr(path, strfind(path,'\\',1,strlen(path)), strlen(path));
 
        if (found)
        {
            msg += strFmt("\n%1 [%2]", conPeek(c, i), conPeek(c, i+1));
        }
        else
        {
            if (path == #ClassesPath+'\\'+classstr(Global)+'\\'+staticmethodstr(Global, info) ||
                path == #ClassesPath+'\\'+classstr(Global)+'\\'+staticmethodstr(Global, error)||
                path == #ClassesPath+'\\'+classstr(Global)+'\\'+staticmethodstr(Global, warning) ||
                path == #ClassesPath+'\\'+classstr(Global)+'\\'+staticmethodstr(Global, checkFailed))
            {
                found = true;
            }
        }
    }
 
    ts.TraceEvent(System.Diagnostics.TraceEventType::Verbose, 0, msg);
}
 
Exception add()
{
    Exception ex;
    …
    //return super(_exception, (buildprefix?getprefix():'')+_txt);
    ex = super(_exception, (buildprefix?getprefix():'')+_txt);
    this.addTraceCallstack(_txt);
    return ex;
}

We’ve created a new TraceSource instance with the same name as used for standard infologs, which is completely valid. In addTraceCallstack(), we get X++ callstack, drop calls related to infolog handling, format the message and log it as a trace event of type Verbose. This method is triggered by add(), after processing the infolog message itself.

Any infolog message logged through the tracing infrastructure will be followed by another massage with X++ call stack.

Infolog - Callstack You’ll have to set switchValue in configuration file to Verbose. You could also log the call stack with the same severity as the original message – it’s all up to you.

If you don’t have enough, you can also check some new features for tracing in .NET 4.5.