SysOperation-Klassen per Job erstellen

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.

ScreenshotFü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

Kommentar hinzufügen
 
 

 

 
 
 
Beiträge des aktuellen Monats
Oktober 2019
MoDiMiDoFrSaSo
 123456
78910111213
14151617181920
21222324252627
28293031 
 
© 2006-2019 Heinz Schweda | Impressum | Kontakt | English version | Mobile Version
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