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 ()
• 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
Thanks for comments.....