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:
• Member variables
• Pack/unpack (aka serialization)
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).