Tuesday, August 30, 2011

10-Minute AX 2012 App: WPF

Powerfully simple put to the test. In this first article, we will create a WPF (Windows Presentation Foundation) application, which will query AX by consuming the system services. The system services are services that provide access to the AX metadata, queries, etc. We will achieve this in 10 minutes (or less... post your time in the comments!).

Ingredients:

* Visual Studio 2010
* Access to an AX 2012 AOS


To get started, open Visual Studio 2010 and create a new project. Under C# / Windows select the "WPF Application" project type. I named this project "DAXMusings.AXWPF".



So, next up, we want to add a service reference to our AX WCF system query service. To do this, you will need to find out what address/port your AOS uses for the WSDL. On a default install, the WSDL address will be http://AOSSERVERNAME:8101/DynamicsAx/Services/QueryService . You can test this by just typing in that URL into a browser and see if you get the XML schema definition of the service (WSDL stands for Web Service Definition Language). If you don't, you can find out what the actual port number is by opening the AX 2012 Server Configuration Utility. The first tab ("Application Object Server") will show the WSDL port. Alternatively, you can open the AX client and navigate to System Administration > Setup > Services and Application Integration Framework > Inbound Ports. You won't find the QueryService in there, but if you look at one of the other services such as AxClient, it will tell you what the WSDL URL is. Just replace the "AxClient" with "QueryService".

Once you figured out your WSDL URL, go back to Visual Studio and right-click on the "References" node in your project, and select "Add Service Reference".



Enter your URL, and hit "GO". This will fetch the WSDL and show you what it has in store. Just enter a namespace (I'm using "QueryService" here, we'll use this in code so you may want to follow me on this name) and click OK.



Next, we'll add a DataGrid to the screen. In the XAML file, add one line to add the control. We'll name it "MyDataGrid" (which is pretty irrelevant for this code), and we set its ItemsSource to "{Binding}".



Lastly, we'll add some code in the MainWindow.xaml.cs which should have opened by default when you created the project. We'll add some code in the MainWindow() method.



Before I explain all that's going, and before I've passed the 10 minute mark blabbing about the details, give it a spin. If you followed to the letter and your URL is correct, you should see a data grid with all the query's fields, and whatever rows of data you have in your AX environment.

Click. What's your time? :-)



So, the AX QueryService returns a standard System.Data.DataSet .NET object. The DataGrid control has the magic. First, we set it to Binding. Notice that we didn't tell it WHAT to bind to. This is WPF feature where certain properties get passed down from parent controls to child controls (looking at the XAML file, the Window is the root, Grid is the first child, DataGrid is the child of Grid). So, from our code, we set "this.DataContext", where "this" is the window. So the Window passes down the DataContext to all its children (see this MSDN article for more information on that). DataGrid also has a property called "AutoGenerateColumns", which is set to true by default (so we don't explicitly set it in the XAML). The feature will auto generate columns for each column contained in the dataset.
Also note the "Paging" object. We use positionbased paging, which requires an explicit sort on the query. So if you try to use a different query, you may see the following error: "Paging is not supported for queries that do not have an ordering property". You can pass in a NULL reference instead of creating an instance, however, some queries return so much data it will hit the maximum buffer size of your WCF service consumer. You can change these buffer sizes in your app.config file which is contained in your project.

There's more you can do here of course. For one, you can hook up the paging object to some controls so you can actually page through the results. You could explore some of the other system services, for example to get a list of queries in the system, and then allow the user to select one and get the results.

No was that powerfully simple or what?!

Monday, August 29, 2011

Three 10-minute Powerfully Simple Apps

AX 2012: Powerfully simple, you've probably heard that by now. We've looked at events, managed code, and all sorts of other interesting and powerful new things in AX 2012. Now, I'd like to show you just how powerful (and simple) some of these new things really can be. In the next few days, I will show you three apps using other hot Microsoft technologies. On top of that, these apps will only take about 10 minutes to develop from scratch (or less if you're as good as our team...). That should fit into your 5-minute lunch break, right? You bring lunch, I'll bring the code (-walkthrough).

So, what apps will we be creating? Well, here's the list of ingredients: WPF (Windows Application), Windows Azure (Cloud) and Windows Phone 7 (Mobile).

Tuesday, August 23, 2011

AX2012 Developer Resources

For those of you who don't visit my Dynamics AX Musings blog directly, here's what you've been missing out!
I have been posting quite a few lengthy articles on the subject of AX 2012, highlighting new features and actually walking through full code examples (Business Operation Framework, X++ in CLR, Events, Managed Code, Models, etc). Blogs are great, but they are horrible to navigate and don't lend themselves well as references, since they are inherently tough to navigate. To that end, I have setup two "pages", providing a listing of resources available, both articles on my blog as well as external resources such as MSDN articles and Whitepaper downloads.
I keep these pages up-to-date any time I post a new article, so check back regularly if you've been missing a few posts!

The two pages available:

ALM/TFS with Dynamics AX
AX 2012 Developer Resources

Sunday, August 21, 2011

X++ in the .NET CLR : Life in the Fast Lane

You've heard it by now. In Dynamics AX 2012, X++ can actually run in the .NET CLR. What I've often heard is "X++ running on the server runs in IL". Well, that is somewhat true. But it doesn't mean setting your classes or menu items to "RunOn Server" makes them run in IL.

So what runs in the CLR "automatically"? Well, any execution path that starts on the server definitely does. First of all that means, batch jobs... the server runs and executes batch jobs, so they all run as IL. All services run as IL, since they start on the server tier as well. Any "Business Operation" (the RunBase replacement called "Business Operation Framework" - more on that here) also runs as CLR (since it uses the service framework as a back end) So does that mean code started from the AX client never runs as IL, even when it's set to run on the server tier? Well, not automagically. But, we can do some manual magic!

Consider the following method (created on a class I called "XppIL"), it checks whether it is running in the CLR or as interpreted X++ script and outputs an infolog to show you the result. Note it accepts and returns a container. Anyone who has worked with the runAs() method in AX 2009 will recognize this pattern.



Notice the method does not require the "server" modifier (although adding won't harm anything of course). It is just public static, taking basically the default "called from". You can make this "server", but as said earlier, that won't make it run as IL necessarily.

Ok, so how do we now run this as IL? Calling this now from any method (server or client) will just return the "X++ interpreted" message... Well, let's create a Main method on our "XppIL" class and call the static method. But not the old fashioned way, we use the new function "runClassMethodIL". We don't care about passing in or getting out containers, so we'll just pass in a conNull() since we need to pass in something.



To call the runClassMethodIL function, we need to assert the XppILExecutePermission. Also as a good practice we don't use strings for the method and class name, but use the classStr() and staticMethodStr() precompiler functions.
For good measure, we'll also call the method directly, from our Main method that we make run on server, just to make the point "server" doesn't mean IL necessarily.

The last thing we need to do is make sure we generate the CIL so our X++ can run in the CLR. No need to compile the whole thing again, you can do an "incremental" compile to just compile any changes you made since the last CIL generation.



We can run our class now. And, as expected, our output looks as follows.



Great! That worked. But, how do we now debug this? Well, in Visual Studio of course! Few things on that. First, remember the IL runs on the server, so you need to debug the server process. Secondly, to debug the server service, your Visual Studio should run as administrator (elevated permissions). If it's not running as such, Visual Studio will ask you. Let me show you. Open Visual Studio. On the debug menu, select "Attach To Process".




From the list, select the Ax32Serv.exe. If it's not on the list, make sure you have the "Show processes from all users" and "Show processes in all sessions" checked. Click the "Attach" button. If your Visual Studio is not running as administrator, you will get the prompt as shown below. Once you say "restart under different credentials", and then click "yes" to allow it, Visual Studio will restart in administrator mode. You will have to repeat the above steps to attach the debugger to the Ax32Serv.exe.




Next, open the application explorer (View / Application Explorer). Navigate to Classes and find the XppIL class. Expand the class and double-click on the "RunCode" method. This will open the X++ code in Visual Studio (not editable, and unfortunately no code highlighting either).




Let's put a breakpoint in this code. Put your cursor on the line with "if (xSession" and press F9 to put a breakpoint on that line. This will put either a red dot or a red circle in front of the line. If you get a red circle (shown below), you will notice it has a little warning icon in there as well. If you hover your mouse over it, you will notice the tooltip says "The breakpoint will not currently be hit. No symbols have been loaded for this document". Basically this happens because AX will not load all assemblies at startup, but rather load them on first use. So, as soon as we start our class, you will see Visual Studio's output window (if you have that open) show assembly loading, including the IL compiled X++. So, get back into AX and run the class again...



Windows should switch you to Visual Studio and you will see the breakpoint hit there. Notice you have full capability here, including stack trace, variable watches, etc. One MAJOR point to remember here is that Visual Studio supports conditional breakpoints. That's right. The feature you've been waiting for.

As far as debugging in Visual Studio goes, the shortcuts are the same as in AX. F5 is continue, F10 is Step Over, F11, is Step Into, etc.


Have fun in the fast lane!

Thursday, August 18, 2011

From RunBase to SysOperation : Business Operation Framework (Cont'd)

So, yesterday I started writing about the Business Operation Framework (BOF) - aka the SysOperation framework. We walked through a full example of taking a basic RunBase class and turning into a Business Operation Framework MVC pattern. The example turned out fairly straightforward once you get the concepts down. The two things we were missing though, was the ability to add a query to the Operation, as well as manipulating the auto-generated dialog of the BOF, which is based on the Data Contract and its data member data types. This article builds further on that code, so if you haven't read it yet, please check the previous article first.

Let's start with the query. Since all inputs that are being passed down to your operation are on the data contract, we can safely assume the query will need to go onto the data contract as well. Now, the data contract has accessor methods for every member you wish to expose, and those members are base AX types (string, int, real, etc). So how will we pass a query? Well, traditionally queries are "packed" (aka serialized) into a container. Containers however are not part of the CLR and so we need a more basic type to serialized our query to. So, BOF basically serializes (encodes) the query to a string. Let's look at the easy way to do first, which is basically all automatic.

Assuming we have a query in the AOT called "CustBaseData" (this query is available in standard AX for your convenience). All we need to do add that query to our Data Contract is add a variable in the classDeclaration for the encoded query string, and create a data member on the data contract class of type string, and add a special attribute for the query (on top of the datamember attribute we have to add anyway):




The AifQueryTypeAttribute specifies the name of the argument variable that the encoded query will go into, and you specify what query in the AOT it is based on (CustBaseData in this case). This is already enough to make the query show up on your dialog, as it would with RunBase (a dialog with select button, etc).
Now of course the question is, how do we retrieve (decode) this encoded query string in our operation "CoolOperation"? Well, basically we're not encoding the query itself necessarily, we're encoding and decoding a container (=packed query).
For this, AX has two methods available:

SysOperationHelper::base64Encode()
SysOperationHelper::base64Decode()

So, to retrieve the packed query, we just need to decode the parmQuery() string of the data contract to a container, then unpack that container to a query... So, let's add some code to our previous CoolOperation method:



Here we decode the parmQuery() string to container and immediately pass that to the new Query() method to unpack that there. Further down, we create a new QueryRun object base on that query and just iterate the result set and output the customer account number. Before we can run this, we need to run the Incremental CIL Generation! Then, go ahead and try it! To limit the amount of output, I basically clicked the "Select" button and selected the first customer account as a filter.




Ok, so that worked beautifully! Now you know how to add a query to your Business Operation. So how about customizing the dialog? In a RunBase class, we can add groups to the dialog, and order dialog controls by changing the order of adding of fields to the dialog. This can also be achieved with BOF, by adding some attributes to the data contract.
So, the original dialog showed the two integers and then the title. Let's add a group for the numbers, and a group for the title. And let's sort it so the title group is comes before the numbers. All this information is added as metadata on the contract. First, you "declare" the group in the classDeclaration of the data contract:



The attribute takes a name for the group (control), a label (I used a hard coded string here, but you should use a label such as @SYS9999), and a third parameter specifying the order. Now, for each data member, we can say what group it belongs to, and give it a sorting number within the group:


LastNumber
Title

The order in which you add the attributes doesn't matter. The query doesn't take a group since it's on the side with the filter fields and the select button (ok, I had to try this to see what would happen... nothing. Adding a group attribute on the query doesn't do anything. You read it here first!).
Anyway, now we have groups, but our two integer fields are still called "Integer" and "Integer". So how do we set label and helptext like we used to do on RunBase dialog fields? Well, more attributes! (Again, please use labels in real life code!)


Last Number

Here's what the dialog now looks like:



Admittedly, I haven't figured out how to get rid of the "Parameters" top-level group. I'll be debugging this some time to figure out if there's an easy way to get rid of it. So anyway, this is all great. But is there *ANY* way to build a dialog from scratch, like, the old-fashioned way using dialog.addField() or something?
Well yes ladies and gentlemen, there is. You can create your own "UIBuilder" class by extending the SysOperationUIBuilder class. By default, the SysOperationServiceController class uses the SysOperationAutomaticUIBuilder class, which examines your data contract(s), the attributes used (for groups etc), and builds the dialog from that automatically. But, you can create your own builder. To make the BOF use your UI builder, you guessed it, we can attach the builder to your data contract using... an attribute:

SysOperationContractProcessingAttribute(classStr(YOURUIBuilderClassName))

Unfortunately, again, the post is running a little long, so I'll owe you an example of this some time. Feel free to test out the UI builder class, if you search the AOT for the SysOperationContractProcessingAttribute string in the classes, you will find some examples of this in standard AX. Happy coding!

Wednesday, August 17, 2011

AX 2012 Manuals and Training

Before continuing on the Business Operation Framework post, I wanted to share that you can now find some official AX 2012 manuals on PartnerSource, if your organization has access to the learning content on PartnerSource.

The following manuals have been released as of August 17:

- Finance 1
- Finance 2
- Distribution and Trade
- Supply Chain Foundation
- Development 1

You can find these manuals on Partner Source here.

Additionally, Streamline Systems has scheduled public classes by Microsoft Certified Trainers (schedule is here), as well as offerings for private classes on-site or remote, for which you can contact training@streamlinesys.com.

Tuesday, August 16, 2011

From RunBase to SysOperation : Business Operation Framework

There's been a lot of talk about the Business Operation Framework (or SysOperation), how it is the next iteration and future replacement of the RunBase and RunBaseBatch framework. I say "future replacement" since there is plenty of RunBase to go around in the standard application. But we'd all be fools not to take advantage of this new framework in AX. There is a bit of information to be found about this new framework online already. Rather than just a straightforward walkthrough, I will make the comparison between RunBase and SysOperation and you will see making the transition is not that difficult.

The SysOperation framework is basically an implementation of the MVC pattern. The isolation of the parameters (model), the dialog (view) and the code that runs (controller) is the essence of the pattern. If you think about RunBase and these three elements, you will realize how they are intertwined within the RunBase framework:

Model:
• Member variables
• Pack/unpack (aka serialization)

View:
• Dialog()
• GetFromDialog()
• PutToDialog()

Controller:
• Prompt()
• Run()

The framework leverages the services framework for this. A service in AX has a data contract. This is basically an AX class with special attributes. The class specifies accessor methods ("parm" methods in AX-speak), again decorated with attributes, which let you set and get member variables. This specifies the data that can go into your process (aka "operation"), and can also specify a return value for it. The operation itself is just a method that takes a data contract, and returns a data contract. This basically classifies it as a custom service. And indeed, if you use this framework, your operation can in fact be used as a service over AIF. You can see the SOA emerge, a hint of where AX is going. Anyway, distractions aside, we now have a model, we have a process, now we need a controller and a view… Easily enough, the view is automatically created based on your data contract (although you can create your own). The controller is simply an inherited class that doesn't even need to do much.
So, what I decided to do is show you a RunBaseBatch class, and then help you convert the class into a SysOperation class. This class lets you input a title and two integer numbers. The run() method will use the title as an infolog prefix, then output the numbers, and the sum of the two numbers. Exciting stuff indeed. So let's dissect this class.

The inputs of this class are the title and the two numbers. My ClassDeclaration of this class looks as follows:



Also note the DialogFields in here (the "View"), and the macros #CurrentList and #CurrentVersion which are used in the pack() and unpack() methods, basically the "serialization" of the input data. To convert this into a BOF pattern, we will create a Data Contract with our three arguments.



For each member variable (since in AX they are always private), we will create an accessor method. Again here we need to decorate with attributes.





So, we have now created our input Data Contract. Next, we want to create the operation, which is basically the run() method of the RunBase. Compared to RunBase, the operation will not have the member variables for our inputs, but rather it will take the data contract as an argument. Below shows the original run() method, and the new operation method. We'll also make the operation class RunOn = Server. Other than that, it's a regular class with a method, no special inheritance, interface implementation, or anything.





Ok, so we now have the input Data Contract, we have the operation that takes the data contract and runs the functionality. How about the dialog? Well, that will get auto-created by the controller. So, how about that controller? Another class!



Ok, here's where it gets interesting. Technically, we wouldn't even need our own controller class. The base controller class which we're extending here (SysOperationServiceController) has some extra logic in its base method, that reads parameters from the Args argument. This will allow you to just create your own menu item (see MSDN article on BOF usage), point it to the class. The parameters field needs to be set to your class and method name. In our case it would be DAXMusingsService.CoolOperation (notice the period in between the class and method), the enumtype to SysOperationExecutionMode and then pick your enum value, default is Synchronous (more about this later) - see an example here. So why am I telling you all this when we were creating our own class?
Well, the SysOperationServiceController's constructor (the "New" method in AX) takes the service class name, operation method name and the SysOperationExecutionMode enum as arguments. Since we specifically created our controller class to run the specific operation, we can "hardcode" the class and method name (using best practices of course: pre-compiler methods classStr and methodStr). To accomplish this, let's just create a static construct method. We'll still leave the option of passing in an execution mode so you can do some testing on your own later.



Ok, so we created a handy static construct method. Are we there yet? Yup, pretty much, all we need is a static Main method to execute our controller class.



One more thing left to do. Since services, including BOF services, run X++ in the CLR, we need to generate CIL. Rather than doing a full compile (which you should have done when you installed AX!), we can just generate incremental CIL.



Alright, ready to rock and roll. Run the class!




Cool, that worked! So, no thanks to the length of this article so far, that wasn't bad at all, was it?

Of course, the AX-dialog aficionado in you is already complaining about the auto-generated dialog . For one, in this example the two integers with the same data type have the same label. Secondly, RunBase(Batch) classes need to deal with queries, so how does that work?

Well, since this article has turned out longer than expected, I will have to politely ask you to tune in again tomorrow for the next article. (If you can't wait here's a tip: more attributes).

Tuesday, August 9, 2011

AX2012 Managed Code - Part 3: Unfortunate Limitations

Part 1: Get Set Up
Part 2: Interop Bliss
Part 3: Unfortunate Limitations


After all the glory in the last article, I'll have to end by showing you one unfortunate feature that does not work. In the previous article, we have created some managed code to which we were able to pass AX objects. Those objects translated perfectly into the proxies we were using in managed code. All this worked seamlessly. We also used a managed handler, accepting the XppPrePostArgs class, also a proxy. The event fired and the XppPrePostArgs instance was passed into our managed handler perfectly.

As you have probably read or at least heard mentioned before, there are pre/post events, and there are delegates (aka "coded events"). The pre/post can be handled in two ways. First, as in the previous example, by creating a handler that accepts the XppPrePostArgs class. The second way to handle pre/post events is by using a "lightweight handler". The lightweight event handler just needs to match the argument signature of the method it subscribes to, and return void. These handlers are called "lightweight", because they effectively give you no access to the event sender object, no access to the return value, and depending on the type of arguments (certain arguments are by default passed in as references) no means of changing the arguments to the method in case of a pre-handler. The advantage however, is that you don't have to access the arguments by means of index or name of variable, but rather get compile-time checking of your matching method arguments.
The other type of event, called a delegate, is also named a coded event because it is the original coder's intent to tell the outside world that something has happened, and allow it to react to that event. A regular convention in the .NET world, which is also recommended by the AX compiler team, is to have two arguments to the delegate: the sender object and an arguments class. The reason you want to pass an arguments class is simple. It allows the original developer of the delegate to extend the arguments being presented to event subscribers, without changing the signature of the delegate. This way existing subscribers don't break, and newer subscribers can use the extensions on the arguments class. For this purpose, AX now has a class named "XppEventArgs", which you are encouraged to inherit from to create your own event arguments.

So back to the code. Recall that in Part 1 of this series, you created a delegate on an X++ class that looked like this:



Also recall, that in part 2, you created the following managed event handler in Visual Studio:



coincidentally, they have the same signature. So, as per the definition of a delegate handler, you expect you can hook up this handler to the delegate. Well, unfortunately this is NOT supported. If we try to set this up (right-click the X++ delegate, select "New Event Handler Subscription") as a managed handler, the method dropdown is empty (it doesn't find a matching handler in our HandlerClass):



If we try to actually type in the method name of our handler manually ("ComplexHandler"... remember, ANYTHING related to managed code is case sensitive, class names, namespaces, method names, etc), you will get a compile error:



The error message actually reads "The method ComplexHandler in class -snip- has parameters of X++ complex types. It cannot be used as an event handler". I find this an interesting message, since it seems to imply that it's the handler's fault for having the wrong types, whereas it's really the X++ delegate's complex type arguments that make it incompatible with any managed handler. When I asked Microsoft about this, the simple answer was "it's not supported". Although I love the managed code interop, and I love the eventing story, apparently the two cannot be fully reconciled…

But WAIT! Did we not see in the previous article that we were able to call this ComplexHandler method from X++ code? Well yes, we did see that. And don't delegates work with X++ code in any shape or form? Well yes, they do.
So what if… we create an X++ handler, that calls the managed code method ComplexHandler and just "forwards" the arguments? Let's add a new static method to our DAXMusings class which we can use for this, let's call it "proxyHandler", and let's call the managed code within it… should work?



If you try to save this… you will get a compile error "Syntax error". Ok, I did this on purpose of course. This is a minor "glitch" I would say. The semi-colon you no longer need in AX 2012 code? Well, you still need it if your first statement is managed code (basically all of the "rules" of when it was needed in AX 2009 X++, still apply for the managed code statements in AX 2012). So, let's add the semi-colon and save the method, which will compile at this time.



Next, add the handler to the delegate. You can simply drag&drop the "proxyHandler" method onto your delegate in the AOT. All the properties will be setup correctly that way. DO NOT FORGET to rename the handler. If you want to know why this is important, check out previous articles here and here.




Ok, so this saves and compiles! What if we execute it?



Yup, our managed handler now also gets called from the delegate! So, this is quite interesting. Hooking up the managed handler directly does not work, but using this X++ "proxy" handler, yup, that works… As I stated at the beginning of this series, the AX compiler team are real smart people, so I'm sure there are good reasons for this limitation. However, we have to admit that we are missing this one feature to really call .NET a first class citizen in AX 2012. As for us, are we going to be using managed code in our implementation solutions? Absolutely.

I'm still absolutely thrilled about the managed code interop! Perhaps we can't call .NET a first class citizen, but we can sure call it a first class green card holder.

AX2012 Managed Code - Part 2: Interop Bliss

Part 1: Get Set Up
Part 2: Interop Bliss
Part 3: Unfortunate Limitations

In part 1 of this series, we setup some base code in AX, and set up Visual Studio with a project containing some AX proxy classes, ready to deploy and run code from Visual Studio. This article (part 2) builds on top of that code.


Ready for some action? I thought so! First, we'll create a pre/post event handler and hook it up to our Run() method as a managed pre handler. So, with the code from the previous article in place, let's add some code in Visual Studio. If you didn't save a solution you can re-open, you can open the Visual Studio project form the AOT. Open AX, and in the AOT go to Visual Studio Projects > C Sharp Projects and find the DAXMusings project. Right-click the project and select "edit". This will open the project in Visual Studio for you.
So, in the HandlerClass.cs file, we will add a new method to the HandlerClass. Handler methods (whether they are X++ or managed) that you will add in the AOT, have to be static and public of course, so they can be called from the event sender. We will use a "standard" pre/post handler, not a lightweight handler, so the only argument we are expecting is XppPrePostArgs. For more information on XppPrePostArgs and how to use it, also see my models and events series (specifically the second part). Just to see our code working I retrieve the calling object (getThis()) from the XppPrePostArgs class and output the type to the infolog using our Global class proxy. The interesting piece here is that getThis() returns an "object". We need to get this into our proxy. Remember the proxy is a "wrapper" around the AX object, and that means we can't CAST the getThis() returned object to our DAXMusingsEvents class, but we need to create a new proxy class and pass it the AX object. It's important you understand why and how this works, because it can lead to some confusion if you're not careful.



Let's build and deploy this to our client so we can add it as a handler in the AOT. Just press F5 in Visual Studio. In AX, open the AOT and find our DAXMusingsEvents class. Right-click on the Run() method and select "New Event Handler Subscription". Open the properties for the handler. Give it a proper name (this is important, check this post so see why, also check the models and events series), and set the "EventHandlerType" property to "Managed". For the class name, we need to include the full namespace handle to the class. So the class property will be set to "DAXMusings.ManagedHandlers.HandlerClass". As soon as you do that, the dropdown for the "Method" property will show you the "compatible" methods on the class. Just select the "PrePostHandler" method and save and compile the class.





If you now run your class, you should see the follow infolog:



If you started AX using F5 in Visual Studio, you can put a breakpoint in your C# method and run the class in AX again, to see how the debugging works.
So, this works perfectly. You could re-implement the code from the models and events articles to use a managed handler for the validateField instead of the X++ handler.

Let's try some regular interop. How about we make a C# method which we will call from X++ code? We'll create one method here which we can later re-use for other purposes. Close the AX client and go back into Visual Studio. In the HandlerClass, we will add the following method:



Hit F5 to build, deploy and start AX. Find your DAXMusingsEvents class in the AOT, and right-click > New > Method. We'll call the method "CallManagedCode". Inside, we will create an instance of an XppPrePostArgs class, set an argument "string", and call the static C# method we created previously.



Next, we'll call this method from the existing Run() method. Open up Run() and add the call to "CallManagedCode". When you now run the class, you should see the PrePost handler fire, but also our call to our method.




Let's review what this means. The "CallManagedCode" method, passes "this" (DAXMusingsEvents instance) and an instance of the XppPrePostArgs. Our C# managed method, accepts our PROXY classes for DAXMusingsEvents, and our proxy for XppPrePostArgs. As you can see from the output in the infolog, this works perfectly! You get the proxies in your C# code as expected. I encourage you to play around with this. Debug, try to create a new instance of an AX class in C# and return it from a method, etc. Interop is great. These were basically the missing features in previous versions of AX. Instances of CLR objects and/or AX objects crossing the boundaries back and forth.

As cool as this is, we'll have to end on a sad note in our part 3 of the series, showing you the unfortunate limitations of these new features.