Wer schon einmal eine Funktion mit Hilfe des SysOperation-Frameworks umgesetzt hat weiß, daß das Erstellen der bis zu vier notwendigen Klassen doch etwas Tipparbeit ist.
Für faule effiziente Zeitgenossen habe ich deshalb den nachstehenden Job entwickelt, mit dessen Hilfe sich diese zeitraubende Arbeit deutlich verkürzen kann.
Man gibt im Job über die Variable baseClassName einen "Basisnamen" für seine Funktion an und der Job erstellt alle vier Klassen (Controller, Dataprovider, UIBuilder und Service).
Zusätzlich gibt es im Job noch eine Map namens dataContractParmsMap aus der - soferne man Inhalte in diese Map eingefügt hat - bei der Generierung des Dataproviders auch gleich passende parm-Methoden erstellt werden. Im Job enthält die Map zwei Einträge für eine Debitorenkontonummer und eine Artikelnummer.
Wie diese Klassen aussehen bzw, welche Methoden generiert werden, zeigt der obige Screenshot.
static void dev_createSysOperationClasses(Args _args)
{
str 40 baseClassName = "DEV_SysOpTest"; // Your base class name
str attributes;
Set interfacesSet;
str 100 uiBuilderName;
str 100 dataContractName;
str 100 serviceName;
str 100 controllerName;
ClassNode createdClassNode;
SetIterator interfaceIterator;
str runServiceMethodStr;
str customMethodStr;
ClassBuild classBuildCompile;
Map dataContractParmsMap;
MapEnumerator dataContractParmsME;
Source parmMethodSourcecode;
xppSource xppSource;
Counter c;
Dialog dialog;
DialogField df_baseName;
#AOT
ClassNode createSysOperationClasses(str _newClassName, str _extendsClassName, Set _implementsInterfaces, str _attributes)
{
// Inspired from SysClassWizard::createEmptyClass()
ClassBuild classBuild;
str header;
SetIterator implementsIterator;
;
if(_attributes)
{
header += _attributes + '\n';
}
header += 'public class '+_newClassName;
if (_extendsClassName)
{
header = header + ' extends '+_extendsClassName;
}
if (_implementsInterfaces && _implementsInterfaces.elements() > 0)
{
implementsIterator = new SetIterator(_implementsInterfaces);
implementsIterator.begin();
header = header + ' implements ' + implementsIterator.value() ;
implementsIterator.next();
while (implementsIterator.more())
{
header = header + ', ' + implementsIterator.value();
implementsIterator.next();
}
}
classBuild = new ClassBuild(_newClassName);
classBuild.addMethod(methodStr(SysClassWizard, classDeclaration), header + '\n{\n}\n');
classBuild.classNode().AOTcompile(1);
return classBuild.classNode();
}
void createAbstractMethods(ClassBuild _newclass)
{
// Inspired from SysClassWizard.createAbstractMethods()
DictClass dictClass;
DictMethod dictMethod;
DictMethod dictMethodFound;
SysDictClass sysDictClassNew;
int i;
int methodActualClassId;
;
dictClass = new DictClass(className2Id(_newclass.name()));
sysDictClassNew = new SysDictClass(dictClass.id());
while (dictClass.extend() > 0)
{
dictClass = new DictClass(dictClass.extend());
for (i=1; i<dictClass.objectMethodCnt(); i++)
{
dictMethod = dictClass.objectMethodObject(i);
if (dictMethod.isAbstract())
{
// check if method is alreade implemented
methodActualClassId = sysDictClassNew.methodsActualClassId(dictMethod.name());
if (methodActualClassId != sysDictClassNew.id())
{
dictMethodFound = new DictMethod(UtilElementType::ClassInstanceMethod, methodActualClassId, dictMethod.name());
if ( dictMethodFound && dictMethodFound.isAbstract() )
{
_newclass.overrideMethod(dictMethod.name());
}
}
}
}
}
}
void createInterfaceMethods(str _interfaceName, ClassBuild _newclass, boolean _overrideParentMethods)
{
// Inspired from SysClassWizard.createInterfaceMethods()
ClassNode sourceNode;
TreeNodeIterator methodIterator;
MemberFunction method;
str todoString;
;
sourceNode = TreeNode::findNode(#ClassesPath + '\\' + _interfaceName);
if (!sourceNode)
{
Box::warning("@SYS65036");
return;
}
methodIterator = sourceNode.AOTiterator();
if (methodIterator)
{
method = methodIterator.next();
while (method)
{
if (method.treeNodeName() != methodStr(SysClassWizard, classDeclaration) &&
(_overrideParentMethods) ||
(!_newclass.getMethodImplementation(method.treeNodeName(), true)))
{
_newclass.addMethod(method.treeNodeName(), method.AOTgetSource());
todoString = strFmt('\n //TODO: %1 \n', strFmt("@SYS73931", method.treeNodeName()));
_newclass.addSourceToMethod(method.treeNodeName(),todoString);
}
method = methodIterator.next();
}
}
}
uiBuilderName = strFmt("%1%2", baseClassName, "UIBuilder");
dataContractName = strFmt("%1%2", baseClassName, "DataContract");
serviceName = strFmt("%1%2", baseClassName, "Service");
controllerName = strFmt("%1%2", baseClassName, "Controller");
// Use this map to create parm-Methods, if needed
dataContractParmsMap = new Map(Types::String, Types::String); // 1=EDT, 2=Name of variable
//dataContractParmsMap.insert("CustAccount", "myCustAccount");
//dataContractParmsMap.insert("ItemId", "myItemId");
dataContractParmsME = dataContractParmsMap.getEnumerator();
// Validation
if( !baseClassName)
{
throw error(Error::wrongUseOfFunction(funcName()));
}
if(strLen(uiBuilderName) > 40)
{
setPrefix(uiBuilderName);
throw error(strFmt("@SYS335263"));
}
if(strLen(dataContractName) > 40)
{
setPrefix(dataContractName);
throw error(strFmt("@SYS335263"));
}
if(strLen(serviceName) > 40)
{
setPrefix(serviceName);
throw error(strFmt("@SYS335263"));
}
if(strLen(controllerName) > 40)
{
setPrefix(controllerName);
throw error(strFmt("@SYS335263"));
}
runServiceMethodStr = strFmt(@"[SysEntryPointAttribute(true)]
public void runService(%1 _dataContract)
{
if( !_dataContract.validate())
{
throw error(error::wrongUseOfFunction(funcName()));
}
}", dataContractName);
// ### UIBuilder
interfacesSet = new Set(Types::String);
attributes = "";
createdClassNode = createSysOperationClasses(uiBuilderName, "SysOperationAutomaticUIBuilder", interfacesSet, attributes);
// ### Datacontract
interfacesSet = new Set(Types::String);
interfacesSet.add("SysOperationValidatable");
interfacesSet.add("SysOperationInitializable");
attributes = strFmt("[\n\tDataContractAttribute\n, SysOperationContractProcessingAttribute(classStr(%1))\n]", uiBuilderName);
createdClassNode = createSysOperationClasses(dataContractName, "", interfacesSet, attributes);
// Create interface methods
interfaceIterator = new SetIterator(interfacesSet);
interfaceIterator.begin();
while (interfaceIterator.more())
{
createInterfaceMethods(interfaceIterator.value(), new ClassBuild(createdClassNode.AOTname()), false);
interfaceIterator.next();
}
//validate()
createdClassNode.AOTsave();
new ClassBuild(dataContractName).addSourceToMethod("validate", "\n\treturn true;");
// Add parm()-Methodes from Map
createdClassNode.AOTsave();
while(dataContractParmsME.moveNext())
{
c++;
new ClassBuild(dataContractName).addSourceToMethod("classDeclaration", strFmt("\n\t%1 %2;", dataContractParmsME.currentKey(), dataContractParmsME.currentValue()));
xppSource = new xppSource();
parmMethodSourcecode = xppSource.parmMethod(dataContractParmsME.currentKey(), dataContractParmsME.currentValue());
parmMethodSourcecode = strFmt("[DataMemberAttribute\n\t ,SysOperationDisplayOrderAttribute('%1')]\n", c) + parmMethodSourcecode;
new ClassBuild(createdClassNode.AOTname()).addMethod(strFmt("parm%1", dataContractParmsME.currentKey()), parmMethodSourcecode);
}
c = 0;
// ### Service
interfacesSet = new Set(Types::String);
attributes = "";
createdClassNode = createSysOperationClasses(serviceName, "SysOperationServiceBase", interfacesSet, attributes);
// Add runService()-Method
new ClassBuild(createdClassNode.AOTname()).addMethod("runService", runServiceMethodStr);
// ### Controller
interfacesSet = new Set(Types::String);
attributes = "";
createdClassNode = createSysOperationClasses(controllerName, "SysOperationServiceController", interfacesSet, attributes);
customMethodStr = strFmt(@"public static %1 newFromArgs(Args _args)
{
%1 controller;
controller = new %1(classStr(%2), methodStr(%2, runService));
controller.parmArgs(_args);
return controller;
}", controllerName, serviceName);
// Add newFromArgs()-Method
new ClassBuild(createdClassNode.AOTname()).addMethod("newFromArgs", customMethodStr);
customMethodStr = strFmt(@"public static void main(Args _args)
{
%1 controller;
%2 dataContract;
SysOperationStartResult sysOperationStartResult;
if (!_args)
{
throw error('@SYS25407');
}
controller = %1::newFromArgs(_args);
controller.parmExecutionMode(SysOperationExecutionMode::Synchronous);
sysOperationStartResult =
controller.startOperation();
}", controllerName, dataContractName, serviceName);
// Add main()-Method
new ClassBuild(createdClassNode.AOTname()).addMethod("main", customMethodStr);
// ### Compile all created classes
classBuildCompile = new ClassBuild(uiBuilderName);
classBuildCompile.classNode().AOTcompile(1);
classBuildCompile = new ClassBuild(dataContractName);
classBuildCompile.classNode().AOTcompile(1);
classBuildCompile = new ClassBuild(serviceName);
classBuildCompile.classNode().AOTcompile(1);
classBuildCompile = new ClassBuild(controllerName);
classBuildCompile.classNode().AOTcompile(1);
}
Dieser Beitrag bezieht sich auf die Version: Dynamics AX 2012
Diese Webseite verwendet Cookies, um Benutzern einen besseren Service anzubieten. Wenn Sie weiterhin auf der Seite bleiben, stimmen Sie der Verwendung von Cookies zu.
Mehr dazu
Wer schon einmal eine Funktion mit Hilfe des SysOperation-Frameworks umgesetzt hat weiß, daß das Erstellen der bis zu vier notwendigen Klassen doch etwas Tipparbeit ist.
Für
fauleeffiziente Zeitgenossen habe ich deshalb den nachstehenden Job entwickelt, mit dessen Hilfe sich diese zeitraubende Arbeit deutlich verkürzen kann.Man gibt im Job über die Variable baseClassName einen "Basisnamen" für seine Funktion an und der Job erstellt alle vier Klassen (Controller, Dataprovider, UIBuilder und Service).
Zusätzlich gibt es im Job noch eine Map namens dataContractParmsMap aus der - soferne man Inhalte in diese Map eingefügt hat - bei der Generierung des Dataproviders auch gleich passende parm-Methoden erstellt werden. Im Job enthält die Map zwei Einträge für eine Debitorenkontonummer und eine Artikelnummer.
Wie diese Klassen aussehen bzw, welche Methoden generiert werden, zeigt der obige Screenshot.