Part 1 : Get Set Up
Part 2 : Interop Bliss
Part 3 : Unfortunate Limitations
One of the new features Microsoft is praising, is .NET languages being a "first class citizen" in the AX development eco system. That sure is a big deal, and compared to previous releases of AX the major barriers for effectively using .NET code in AX have been overcome. I won't be doing Peter Villadsen's work justice if I tried to give you an overview of the underlying changes that had to happen to make the interop between X++ and .NET work the way it does today. But suffice it to say, Peter's team has done a great job, and there is plenty to talk about. However, there is one sore point (in my opinion) in this model: managed code handlers for X++ events do not support complex types!
What? Yes, you heard me right. If events (be it pre/post events or delegate ("coded") events) have complex argument types to their methods (anything that's not a base type), they will not allow managed handlers. I'm sure there are good reasons, knowing how savvy the compiler team is. I have not been able to get any explanation for this, but I did get official word that it is indeed not supported. Interestingly, when calling .NET code from X++, you can pass complex types back and forth just fine. On top of that, the Pre/Post handler type using the XppPrePostArgs class as an argument (a complex type…!) does work with managed handlers. But when subscribing a lightweight handler, or subscribing to a delegate, the managed code handlers CANNOT take complex types. If you put two and two together, does that mean if you would create an X++ handler, which on its turn calls the .NET handler "manually", that does work? Yes, that does work. Strange goings-on, but again, I'm sure there's some reason.
So, without further delay, I will show you exactly how to create these managed code artifacts, and show you what is and what isn't supported, and how to "get around it".
First, let's create a new class in Dynamics AX 2012 called "DAXMusingsEvents". We will have a Run method that calls a delegate, we'll need to create that delegate, and a main method to run the class.
To create the delegate, right-click your new class and select New > Delegate, or you can just create a new blank method and type in the method manually as shown below.
I use the default event convention as used in C#. You do not necessarily have to follow this convention, however, there is good reason to do it this way. I have not seen the latest best practice guide for AX 2012 and whether it mentions delegate best practices, but I do know the Microsoft AX compiler team at least recommends using this convention. There is an abstract "XppEventArgs" class which you can inherit from. I'm using the XppPrePostArgs class (which extends XppEventArgs) to avoid creating a new class for this article. Anyway, next we create a run method which calls the delegate
Finally, we need a Main method to run our class, which will instantiate the class and call Run().
This is enough setup in AX. Next, we'll go into Visual Studio 2010. Create a new project and select the "Class Library" project type. I called my project "DAXMusings.ManagedHandlers".
When the project is created, in your solution explorer window, right-click the project and select "Add DAXMusings.ManagedHandlers to AOT".
Next, rename the "Class1.cs" file in your project to "HandlerClass.cs". Visual Studio will ask you if you want to rename the class itself and any references to it. Click YES.
We will also need access to some AX classes, including the DAXMusingsEvents class we created, the XppPrePostArgs class we are using, and also the "Global" class since that contains the info, error, warning functions we can use to put messages in the infolog. To do this, open the "View" menu and open the "Application Explorer". This will open an AOT-type view in Visual Studio. Browse to Classes, and find the DAXMusingsEvents, the XppPrePostArgs and the Global class. For each one, either right-click on the class and select "Add To Project", or drag the class onto your project node. When you have all three, your project should look like the project below.
Almost through the setup. In the application explorer, right-click on the DAXMusingsEvents class and select "Set as startup element".
On your project in the solution explorer, right-click and select properties. In the properties, set the "Debug Target" to "Client", and set "Deploy to Client" to "Yes". Also notice that the property called "Startup Element" is set to your DAXMusingsEvents class due to our previous action.
The "deploy to client" flags that this assembly we're building will run on clients, and consequently needs to be deployed to clients. If your code needs to run on server, or both server and client, make sure to set these flags correctly. Also remember event subscribers need to run on the same tier as the event itself. The "debug target" will allow you to hit "F5" (or menu Debug / Start Debugging"), which will build the code, deploy it, and run the AX Client and attach Visual Studio to the client you've started, allowing you to debug the C# code in Visual Studio when it gets run from AX. You can also just close your client, run build and deploy in Visual Studio, then run AX client manually. If you want to debug then, you can click Debug / Attach to Process from Visual Studio and attach to the AX client, which would give you the same result. The "startup element" will make AX run that element (in this case: our class - note that we didn't need a menu item) as soon as it started up. This allows for a full build-deploy-run client-run code scenario with the F5 command.
At this point, the code is not doing anything. Feel free to run F5 and test things out. Next article, we will start testing!