MSDyn365FO. Code to build cross reference data without a full compile.

After 8.1 there is no need to do code upgrade and build standard code supplied by Microsoft or at least we’ve been told so. However, it’s impossible to refresh cross references without building model with Visual Studio tools and we need this because now hotfixes and monthly updates are cumulative and include binaries and X++ code as well, so cross reference data on dev VMs becomes outdated quite quickly.

While MS is working on actual solution, I dug a bit into xppc that compiles X++ code and build cross references and here you are, this code can be used to build cross references for a module without compile, that is way faster!

using Microsoft.Dynamics.AX.Framework.Xlnt.XReference;
using Microsoft.Dynamics.AX.Metadata.XppCompiler;
using System;

class XRefBuilder
{
    static void Main(string[] args)
    {
        try
        {
            string moduleName = "ApplicationCommon";
            string metaDataPath = @"K:\AosService\PackagesLocalDirectory";

            ICrossReferenceProvider xRefProvider = CrossReferenceProviderFactory.CreateSqlCrossReferenceBatchProvider(".", "DYNAMICSXREFDB", moduleName, true, true, new DiagnosticsHandler());
            xRefProvider.StartBatch();
            new MetadataXRefSweeper( metaDataPath, moduleName, xRefProvider, DateTime.MinValue, new DiagnosticsHandler()).Run();
            xRefProvider.CommitBatch();
        }
        catch (AggregateException ae)
        {
            ae.Handle(ex => {
                    Console.WriteLine(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
                return true;
            });

        }
    }
}

Note catch section, it was a big surprise for me, but standard code has lots of compile issues, for example, KanbanMultiDelete action menu item has EnumParameter property populated but EnumTypeParameter is not. In 2012 days it was not possible to do this, but now you can literally type anything into EnumParameter without specifying enum and save it. Cross reference builder would try to find that enum and throw an exception saying that cannot find an enum with empty name. That’s why I have that catch section and that’s what xppc does, just skip all these errors and probably log them somewhere.

Another gotcha here is OutOfMemoryException that you can get with ApplicationSuite model, so don’t forget to handle it as well.

 

UPDATED:

MetadataXRefSweeper build cross references only for objects like EDT or Tables, but does not cover source code. To build cross references for source code you have to actually compile it 😦

 

 

Advanced cross-reference search.

Cross-references search

Cross-reference is one of the best tools we have in AX that is used by developers daily, however, not everyone is using it for 100%.

Recently, I was asked how to find cross-references for a kernel method that is not defined on a table, because you cannot right click on it 😊   And that’s a good question, often we want to find if there is a call to doUpdate or doInsert somewhere.

In AX 2012 go to Tools -> Cross-reference -> Names and filter by table or method name and then click Used by.

SalesLine.DoUpdate_Names

SalesLine.DoUpdate_UsedBy

Using this form, we can find usage of CRL types as well, for example, Microsoft.Dynamics.IntegrationFramework.  It is defined in AOT under references node, however, from there you cannot find any references.  Names would show you everything!

CLRTypeUsage

What about D365FOE?

As we know, it’s a bit different here. Cross-references data is moved to DYNAMICSXREFDB database.

DYNAMICSXREFDB

And it makes perfect sense because you need to pay for each GB of DB space in Production.

Using next SQL statement, we can find usage of any object. For example, Microsoft.Dynamics.IntegrationFramework :

SELECT
	sourceName.[Path] as sourcePath,
	targetName.[Path] as targetPath,
	[Kind],
	[Line],
	[Column],
	sourceModule.[Module] as sourceModule,
	targetModule.[Module] as targetModule
	FROM dbo.[References]
	INNER JOIN dbo.[Names] sourceName
		ON dbo.[References].[SourceId] = sourceName.[Id]
	INNER JOIN dbo.[Modules] sourceModule
		ON sourceName.[ModuleId] = sourceModule.[Id]
	INNER JOIN dbo.[Names] targetName
		ON dbo.[References].[TargetId] = targetName.[Id]
	INNER JOIN dbo.[Modules] targetModule
		ON targetName.[ModuleId] = targetModule.[Id]
	WHERE targetName.[Path] like '%Microsoft.Dynamics.IntegrationFramework%'

CLRTypeUsage_D365FOE

Where Kind is:

    /// <summary>
    /// Types of Cross References
    /// </summary>
    public enum CrossReferenceKind
    {
        /// <summary>
        /// Type not specified. Used for queries
        /// </summary>
        Any = 0,

        /// <summary>
        /// Indicates that the reference is a Method Call
        /// </summary>
        MethodCall = 1,

        /// <summary>
        /// Type reference
        /// Indicated that the type is used (variable and field declaration, attributes, function return type, etc)
        /// </summary>
        TypeReference = 2,

        /// <summary>
        /// Interface implementation
        /// Indicates that the source entity is implementing this interface
        /// </summary>
        InterfaceImplementation = 3,

        /// <summary>
        /// Class Extended
        /// Indicates that the source entity is extending this class or interface
        /// </summary>
        ClassExtended = 4,

        /// <summary>
        /// Test Call
        /// Indicates that the source entity (test) directly or indirectly calls an application method.
        /// </summary>
        TestCall = 5,

        /// <summary>
        /// Property
        /// Indicates that the source entity has a certain property.
        /// </summary>
        Property = 6,

        /// <summary>
        /// Attribute reference
        /// Indicated that an Attribute is used
        /// </summary>
        Attribute = 7,

        /// <summary>
        /// Test Helper Call
        /// Indicates that the source entity is a test helper.
        /// </summary>
        TestHelperCall = 8,

        /// <summary>
        /// Metadata or code Tag reference
        /// Indicates that the source tag is used on a metadata element, class or a method or a line of code.
        /// </summary>
        Tag = 9,
    }

Let’s try doUpdate:

DoUpdate_UsedBy_D365FOE

As we can see, result is different to AX 2012, where we could search for an individual table, now all Common methods have reference to Common.