How to use X++ Delegates in Dynamics AX 2012
In this post I want to cover some of the core functional aspects of the new delegates feature introduced in AX 2012. X++ delegates expose the publisher -subscriber pattern where a delegate defines a clear contract in a publisher class. This contract is used when an event occurs where the event can be a change of state, where all interested classes receive notification that the event has occurred.
The delegate contract.
The class that contains the delegate as a member is called a publisher class. A delegate must be defined on a class by using the right-click context menu ‘New > Delegate’ and it is important to mention there is no support for delegates on other AOT artifacts, just classes.
A delegate never returns a value. A delegate is always a protected member of the class. The delegate can be invoked only by instance members of the class or derived classes. Here is a declaration of a delegate:
delegate void notifyChange(utcDateTime _dateTime,str _changeDescription)
{
}
The delegate is protected because only the owner should decide when its specific event has occurred. However, if he or she has a good reason to, a public method that invokes the delegate indirectly can be introduced as follows:
public void callNotifyChange(utcDateTime _dateTime,str _changeDescription)
{
this.notifyChange(_dateTime, _changeDescription);
}
There is no limitation on the number of parameters a delegate can declare and there is no limitation on the type of those parameters. (For information about the compatibility of value types between X++ and .NET, see https://msdn.microsoft.com/en-us/library/cc584291.aspx) The delegate body is always empty, because the delegate’s only purpose is to define the contract that subscribers must conform to. A class can contain zero or more delegates.
A delegate cannot be overridden in derived classes. Interfaces cannot have delegates.
Whenever a new event handler is subscribed into a delegate in the AOT, the metadata associated with the delegate changes. These metadata changes require that the delegate be recompiled.
Event Handlers.
Methods that subscribe to an X++ delegate are called event handlers. An event handler can be written in two formats:
- An X++ method
- A method that is managed by the Microsoft .NET Framework (such as in C#)
There are two mechanisms available to subscribe method to an X++ delegate:
- By using the AOT (which creates metadata).
- By using the += eventhandler( … ) syntax.
Event handlers in the AOT.
In the AOT, a method can be subscribed to a delegate by dragging the method onto the delegate. The subscribed event handler method must meet the following criteria:
- The parameters of the subscribed event handler method must match (identical) the parameters of the delegate.
- The event handler must be public.
- The event handler must be static. (This is to avoid a burdensome proliferation of objects during repeated invocations of the delegate.)
As an example, consider a second class to be used as the subscriber named SubscriberOneClass, which contains a method that will be subscribed to the earlier delegate. The method merely writes to the infolog window:
public static void notifyMe(utcDateTime _dateTime,str _changeDescription)
{
info("A notification has occurred at:" +
DateTimeUtil::toStr(_dateTime) +
"Message:" +
_changeDescription);
}
The following image of the AOT shows the method was dragged onto the delegate and is now an event handler of the delegate. We also see the Properties window for the event handler.
The Properties window shows the name of the publisher class that owns the method that has become an event handler. The name of the method is also shown. The name of the delegate itself was manually changed to the more meaningful SubscriberOneNotify.
The most interesting property is ‘EventHandlerType’. Its two possible values are:
- “X++” - means the subscribing method is written in X++.
- “Managed” - refers to .NET methods. (The publisher class must be registered under AOT\References or AOT\Visual Studio Projects, marked for deployment to the client or AOS.)
A delegate cannot be modified in a higher layer. The sequence in which the delegate fires each event handler cannot be specified and may vary between delegate invocations.
Add an event handler programmatically in X++.
Event handlers can be subscribed to a delegate during run time by X++ code. Consider a second subscriber class that is named SubscriberTwoClass. This class has to public methods, but only one is static:
public static void notifyStatic(utcDateTime _dateTime,
str _changeDescription)
{
info("A notification has occurred calling static
handler:" +
DateTimeUtil::toStr(_dateTime) +
" Message:" +
_changeDescription);
}
The following X++ job subscribes both the static and instance methods to the delegate. Note the += operator and the eventhandler keyword:
static void AddAndClassHandlers(Args _args)
{
PublisherClass publisher;
SubscriberTwoClass subscriber;
publisher = new PublisherClass();
subscriber = new SubscriberTwoClass();
publisher.notifyChange += eventhandler( SubscriberTwoClass::notifyStatic );
publisher.notifyChange += eventhandler( subscriber.notifyInstance );
publisher.callNotifyChange(DateTimeUtil::utcNow(),"Call handlers test");
}
In this example the delegate is called through the callNotifyChange public method.
The -= operator over the eventHandler keyword can be as well utilized to remove event handlers previously added. Event handlers in the AOT cannot be removed using this approach at run time.
When the publisher object eventually goes out of scope, the X++ runtime releases the references to all the subscriber objects, so their resources can be reclaimed for the system.
Publisher and subscriber objects must run on the same tier.
The delegate infrastructure does not allow making calls cross tiers. For example, if the publisher object which owns the delegate is running on the client, the event handler method cannot be marked as RunOn server. This system design decision was made because calls across tier boundaries are resource intensive. If an application needs cross-tier transitioning, it can be achieved by using regular X++ method invocation. The application logic would need to encapsulate the behavior.
SysXppEvent and SysXppEventHandler classes.
Dynamics AX 2012 has two new classes that are instantiated whenever event handlers are called by a delegate: SysXppEvent and SysXppEventHandler. The names of these classes can be seen in some debugging scenarios, as is shown in the following image.
As with any other system X++ classes, it is strongly recommended that you neither update nor extend these two classes. Making any change to these classes could cause faulty behavior in the infrastructure that processes delegates, now or in the future.
Comments
Anonymous
August 02, 2011
Why was this change made, I'm trying to see the business logic?Anonymous
August 02, 2011
@Curious: "delegates" are an essential part of an event driven architecture. This is great news for those who want to reuse business logic without overriding, replace, rewrite or shadow existing business logic. It enhances future maintenance of your business application making it even easier to add custom logic to existing logic.Anonymous
August 03, 2011
Among the reasons delegates and events handlers have been introduced in AX2012 is to provide application developers with additional tools to reduce the need of customizations base on editing X++ source code in upper layers, however that implies application code should be designed and written in a such a way that the behavior exposed could be customized by event handlers subscribed to the delegates, passing as part of the contract the enough information and right abstractions (APIs) to make possible the behavior customization easy. Pre and Post event handlers have been introduced with the same reason in mind (Additional reference useful to read is: daxmusings.blogspot.com/.../ax-2012-models-events-part3-power-combo.html)Anonymous
March 12, 2012
Hi, How do I programatically attach to the "update" method on a table. I want to subscribe many instances of my class to this such that I can pickup then status changes within the table occur. Every example I've seen simply referes to delegates on a class, yet in the AOT I can add a event subscription statically - which is of no real use. I need to do this dynamically. ThanksAnonymous
February 03, 2014
I'm wondering how to achieve a x++ statement like 'myDelegate += eventhandler(object.method)' with reflection. Any Ideas?