MSDyn365FO. Add postTargetProcess method to a Data Entity via extension.

Quite a while ago I wrote a blog post about postTargetProcess method that could be added to a data entity for post processing. Previously, you could add it only to a newly created entity, but now you can extend any entity using CoC.

This method is a bit special because it is not declared on the parent object, but is called via reflection by DMFEntityBase class. Full list of method that work in the same way:

  • defaultCTQuery
  • copyCustomStagingToTarget
  • postGetStagingData
  • preTargetProcessSetBased
  • postTargetProcess

DMFEntityBase uses tableHasStaticMethod() to check if a method is defined and DictDataEntity.callStatic() to call it. In the latest PUs Microsoft added support of CoC methods to Dict* API and reflection methods, so now it is possible to use CoC method in this scenario as well.

Extension is quite simple and straightforward, all you need to do is to declare static method with respective signature and do not call next because this method is not defined on the parent object:

final public class MyDataEntity_Extension
    public static void postTargetProcess(DMFDefinitionGroupExecution _dmfDefinitionGroupExecution)
        //do stuff


MSDyn365FO. How-to pass generic types to .Net method


There is a wonderful blog post about Generic types in X++. And as It says, the easiest way to work with generic types in X++ is to create .Net library and expose non-generic interface, because X++ compiler has lots of limitations.

However, if you really want to have some fun and stick to X++, it’s possible with a help of reflection.

Let’s say we have a C# class that accepts  List<string[]> and we want to invoke it from X++:

public class Class1
    public static string myMethod(List<string[]> list)
        string res = string.Empty;

        foreach (var array in list)
            foreach (string s in array)
                res += s;
return res;

We cannot simply declare:

var stringList = new System.Collections.Generic.List<String[]>();

Looks like X++ parser cannot handle “<” and “[” combination, as it gives “Invalid token ‘<‘. ” compile error.

Therefore, instead of 2 lines of code in C#:

var stringArrayList = new System.Collections.Generic.List<string[]> { new string[] { "a", "b", "c" } };
string res = Class1.myMethod(stringArrayList);

We need a bit more in X++:

System.String string;

//array we want to put into a list
System.String[] stringArray = new System.String[3]();
stringArray.SetValue('a', 0);
stringArray.SetValue('b', 1);
stringArray.SetValue('c', 2);

//Type[] array for MakeGenericType
System.Type[] typeParams = new  System.Type[1]();
typeParams.SetValue(string.GetType().MakeArrayType(), 0);

//instantiate System.Collections.Generic.List<String[]>
System.Type listType = System.Type::GetType("System.Collections.Generic.List`1");
var constructedListType = listType.MakeGenericType(typeParams);
var stringArrayList = System.Activator::CreateInstance(constructedListType);

//invoke "Add" using reflection because we cannot cast it back to List<String[]>
var  bindingFlags =  BindingFlags::Instance | BindingFlags::Public;
System.Reflection.MethodInfo methodsInfo = constructedListType.GetMethod("Add", bindingFlags);
System.Object[] params = new  System.Object[1]();
params.SetValue(stringArray, 0);
methodsInfo.Invoke(stringArrayList, params);


X++ is lacking in .Net features and often it’s easier to create intermediate C# class library. However, as you can see, some limitations are caused by X++ compiler that could be overcome using reflection.

D365FOE. FormHasMethod extension for form extension methods.

Recently I have seen multiple people asking how to check if form has method added by extension at run-time.  For form methods we can use Global::formHasMethod but it does not work with form extensions. I advised people to use new metadata API to do this and finally community user Axaptus wrote the code!

I tweaked it a bit to ignore method’s name case as AX does and to exclude private methods. Also I used it to extend standard formHasMethod method

/// <summary>
/// The class <c>Global_Extension</c> contains extension methods for the <c>Global</c> class.
/// </summary>
public static final class Global_Extension
    static boolean formHasMethod(FormRun fr, IdentifierName methodName)
        boolean ret = next formHasMethod(fr, methodName);

        if (!ret)
            ret = Global::formExtensionHasMethod_IM(fr, methodName);

        return ret;

    private static boolean formExtensionHasMethod_IM(FormRun _formRun, IdentifierName _methodName)
        if (!_formRun || !_methodName)
            return false;

            System.Object[] extensions = Microsoft.Dynamics.Ax.Xpp.ExtensionClassSupport::GetExtensionsOnType(_formRun.GetType(), true);

            if (extensions)
                System.Type    formRunExtensionType;
                System.Reflection.MethodInfo    methodInfo;
                //extension methods are always static
                var  bindingFlags = BindingFlags::Public | BindingFlags::Static | BindingFlags::IgnoreCase;

                for (int i = 0; i < extensions.Length; i++)
                    formRunExtensionType = extensions.GetValue(i);

                    var methodsInfo = formRunExtensionType.GetMethods(bindingFlags);

                    for (int n = 0; n < methodsInfo.get_Length(); n++)
                        methodInfo = methodsInfo.getValue(n);
                        if (methodInfo.Name == _methodName)
                            return true;
        catch (Exception::CLRError)

        return false;

Extending standard method has its pros and cons. From one side it will slow down execution of standard code that calls it when method does not exist, but it’s a rare case. From another side it allows you to reuse standard code without changing it and it could be handy in various places where AX looks for a method on a form.

Source code is available on GitHub

AX 7. Accessing private\protected class methods and members from extension code.

All class member variables are protected by default in AX 7, so it is impossible to access them from extensions. It becomes a real problem when you try to extend classes like SalesLineType.

For example, we want to add custom logic on sales line insert event. Call to super() is commented out so we cannot create pre or post event handlers. We may try to create event handlers for SalesLineType.insert() mehtod, but we won’t get access to salesLine buffer because this variable is protected.

There are two options: use overlaying or use reflection.

Overlaying is preferable approach, but today we will talk about reflection to explain this option.

Reflection is usually used for unit testing in case you need to cover protected or private code and it is hard to call this code using public API.

It has bunch of disadvantages:

  • It breaches entire basis of OO programming.
  • Slow performance.
  • Possible issues with future updates. Private methods could be changed at any time.

However, once you may get into situation where it could be the only option so it’s better to know about this possibility.

Let’s try to change some fields on sales line insert using reflection.

Create new event handler class for SalesLineType and subscribe to post insert:

using System.Reflection;

/// Handles events raised by <c>SalesLineTypeEventHandler</c> class.
/// </summary>

public class SalesLineTypeEventHandler
    [PostHandlerFor(classStr(SalesLineType), methodStr(SalesLineType, insert))]
    public static void SalesLineType_Post_insert(XppPrePostArgs _args)
        SalesLineType salesLineType = _args.getThis();

        var bindFlags = BindingFlags::Instance | BindingFlags::NonPublic;

        var field = salesLineType.GetType().GetField("salesLine", bindFlags);

        SalesLine salesLine = field.GetValue(salesLineType);

        if (salesLine)
            salesLine.MyNewField = 42;

Also we can call private or protected method:

var bindFlags = BindingFlags::Instance | BindingFlags::NonPublic;

var methodInfo = salesLineType.GetType().GetMethod(methodStr(SalesLineType, checkQuantityUpdate), bindFlags);

if (methodInfo)
    methodInfo.Invoke(salesLineType,  new System.Object[0]());

You can read more about reflection on msdn

Thanks to Simon Buxton for rising this on yammer.