How to Create SysOperationFramework Using Service Class in Ax 2012



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.

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

class UmeshTestBatchRunBase extends RunBaseBatch
{
    str title;
    int firstnumber;
    int lastnumber;
    DialogField titlefield;
    DialogField firstnumberfield;
    DialogField lastnumberfield;
    #define.CurrentVersion(1)
    #localMacro.CurrentList
    title,
    firstnumber,
    lastnumber
    #endMacro
}

Also note the Dialog Fields 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.

[DataContractAttribute]
class UmeshTestDataContract
{
    str title;
    int firstnumber;
    int lastnumber;
}

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.

[DataMemberAttribute]
public Title parmTitle(Title _title= title)
{
   title = _title;
    return title;
}

[DataMemberAttribute]
public int parmfirstnumber(int _firstnumber=firstnumber)
{
    firstnumber   = _firstnumber;
    return  firstnumber;
}

[DataMemberAttribute]
public int parmlastnumber(int _lastnumber= lastnumber)
{
    lastnumber   = _lastnumber;
    return  lastnumber;
}

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.

class UmeshTestBatchService extends SysOperationServiceController
{
}

public void new(IdentifierName _className  = '',
IdentifierName _methodName = '',
SysOperationExecutionMode _executionMode = 0)
{
    super();
    this.parmClassName(_className);
    this.parmMethodName(_methodName);
    this.parmExecutionMode(_executionMode);
}

public void Operation(UmeshTestDataContract Data)
{
    setPrefix(Data.parmTitle());
    info(strFmt("%1+%2=%3",Data.parmfirstnumber(),Data.parmlastnumber(),
    Data.parmfirstnumber()+Data.parmlastnumber()));
}

public void run()
{
    str title;
    int firstnumber,lastnumber;
    setPrefix(title);
    info(strFmt("%1+%2=%3",firstnumber,lastnumber,firstnumber+lastnumber));
}

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.

class UmeshTestBatchController extends SysOperationServiceController
{
}

public static UmeshTestBatchController construct( SysOperationExecutionMode exeMode = SysOperationExecutionMode::Synchronous)
{
    UmeshTestBatchController controller = new UmeshTestBatchController(classstr(UmeshTestBatchService),
    methodStr(UmeshTestBatchService,Operation),exeMode);
    return controller;
}

public static void main(args _args)
{
    UmeshTestBatchController controller = UmeshTestBatchController::construct();
    controller.startOperation();
}

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.

Run the above class and enter inputs and see output

Related Posts

Previous
Next Post »

Thanks for comments.....