[AX]AX2012 SysOperation框架初窥

做过AX开发的同学们应该对runbase、runbasebatch、runbasereport这些类比较的熟悉,用它们来从用户收集数据、交互或者batch方式执行操作、并纪录用户的输入选项下次运行时初始化这些选项,为此需要重载pack、unpack、diaog、getfromdialog、run、cangobatch等函数。在AX2012中微软引入了新的SysOperation框架,用于替换runbase框架,微软提供了一份长达58页的白皮书介绍如何使用SysOperation框架,在这里下载http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=29215,所用到的示例代码可以到这里下载http://blogs.msdn.com/b/aif/archive/2012/03/17/introduction-to-the-sysoperation-framework.aspx。本文就如何使用SysOperation框架做简单的探讨,先贴下示例代码的第一个例子,这样便于后续的说明。

class SysOpSampleBasicController extends SysOpSampleBaseController
{
}

public ClassDescription caption()
{
    return 'Basic SysOperation Sample';
}

void new()
{
    super();

    this.parmClassName(classStr(SysOpSampleBasicController));
    this.parmMethodName(methodStr(SysOpSampleBasicController, showTextInInfolog));
    this.parmDialogCaption('Basic SysOperation Sample');
}

public void showTextInInfolog(SysOpSampleBasicDataContract data)
{
    if (xSession::isCLRSession())
    {
        info('Running in a CLR session.');
    }
    else
    {
        info('Running in an interpreter session.');
        if (isRunningOnServer())
        {
            info('Running on the AOS.');
        }
        else
        {
            info('Running on the Client.');
        }
    }


    info(strFmt('SysOpSampleBasicController: %1, %2', data.parmNumber(), data.parmText()));
}

public static void main(Args args)
{
    SysOpSampleBasicController operation;

    operation = new SysOpSampleBasicController();
    operation.startOperation();
}

首先是SysOpSampleBasicController类,它继承于SysOpSampleBaseController,后者又继承于SysOperationServiceController,按照白皮书的说明,SysOperation框架的控制类应该直接派生于SysOperationServiceController类,但是目前AX2012版本的SysOperationServiceController类有些问题,所以有派生了SysOpSampleBaseController类来修复这些问题,在以后的版本中SysOperationServiceController会被修复,这里就把SysOpSampleBaseController当作控制类的最基类就行了。

可以看到控制类所要重载的函数少了很多,关键的地方一是在new()函数中parmClassName()指定控制类名称、parmMethodName()指定要运行的方法名称及parmDialogCaption()指定交互对话框的标题,另外一个关键的函数就是showTextInInfolog(),它被作为参数传入parmMethodName(),所以这个函数的名称是任意的,但是更改后记得做一次CIL增量编译,并清空有关user data,否则会得到方法找不到的错误提示。再来看showTextInInfolog()参数用到的SysOpSampleBasicDataContract类:

[DataContractAttribute]
class SysOpSampleBasicDataContract
{
    str text;
    int number;
}

[DataMemberAttribute,
SysOperationLabelAttribute('Number Property'),
SysOperationHelpTextAttribute('Type some number >= 0'),
SysOperationDisplayOrderAttribute('2')]
public int parmNumber(int _number = number)
{
    number = _number;

    return number;
}

[DataMemberAttribute,
SysOperationLabelAttribute('Text Property'),
SysOperationHelpTextAttribute('Type some text'),
SysOperationDisplayOrderAttribute('1')]
public Description255 parmText(str _text = text)
{
    text = _text;

    return text;
}

它和运行SSRS报表收集报表参数的data contract类一样使用[DataContractAttribute]特性,两个parmxxx()方法也加入了一些特性指定在交互对话框上显示的标签名称、帮助信息及显示顺序。

运行只需要调用控制类的startOperation()就可以了,它会调用指定showTextInInfolog()方法。它会弹出对话框收集tex和number两个参数,可以放到batch方法去运行,这就是最简单的SysOperation试用方法,和runbase框架相比得到了很大的简化。

再来看第二个例子,它考虑的问题是如何对交互对话框用户输入的数据做lookup和验证,在runbase框架中我们需要在runbase的dialogPostRun中对dialogField注册重载函数,比如:

public void dialogPostRun(DialogRunbase _dialog)
{
    FormControl control;

    super(_dialog);

    // register overrides for form control events
    numberField.registerOverrideMethod(methodstr(FormIntControl, validate), methodstr(SysOpSampleSimpleRunbaseBatch, numberFieldValidate), this);
    textField.registerOverrideMethod(methodstr(FormStringControl, lookup), methodstr(SysOpSampleSimpleRunbaseBatch, textFieldLookup), this);

}

在SysOperation框架中首先对data contract类添加了SysOperationContractProcessingAttribute特性:

[DataContractAttribute,
SysOperationContractProcessingAttribute(classStr(SysOpSampleSimpleUserInterfaceBuilder))]
class SysOpSampleSimpleDataContract
{
    str text;
    int number;
}

由这个特性指定一个自定义的交互对话框构建器类,没有这个特性,会使用系统默认的SysOperationAutomaticUIBuilder构建器,来看看这个自定义的UI构建器是如何做到对dialogField的lookup和validate的:

class SysOpSampleSimpleUserInterfaceBuilder extends SysOperationAutomaticUIBuilder
{
    #define.lookupAlways(2)

    DialogField numberField;
    DialogField textField;
}

public void postBuild()
{
    super();

    // get references to dialog controls after creation
    numberField = this.bindInfo().getDialogField(this.dataContractObject(), methodStr(SysOpSampleSimpleDataContract, parmNumber));
    textField = this.bindInfo().getDialogField(this.dataContractObject(), methodStr(SysOpSampleSimpleDataContract, parmText));
    // change text field metadata to add lookup
    textField.lookupButton(#lookupAlways);

}

public void postRun()
{
    super();

    // register overrides for form control events
    numberField.registerOverrideMethod(methodstr(FormIntControl, validate), methodstr(SysOpSampleSimpleUserInterfaceBuilder, numberFieldValidate), this);
    textField.registerOverrideMethod(methodstr(FormStringControl, lookup), methodstr(SysOpSampleSimpleUserInterfaceBuilder, textFieldLookup), this);

}

public boolean numberFieldValidate(FormIntControl _control)
{
    if (_control.value() < 0)
    {
        error('Please type a number >= 0');
        return false;
    }
    return true;
}

public void  textFieldLookup(FormStringControl _control)
{
    FormStringControl       companyControl;
    SysTableLookup          tableLookup;
    Query                   query = new Query();

    companyControl = _control;
    tableLookup = SysTableLookup::newParameters(tablenum(DataArea),companyControl);
    tableLookup.addLookupfield(fieldnum(DataArea,Id),true);
    tableLookup.addLookupfield(fieldnum(DataArea,Name),false);

    query.addDataSource(tablenum(DataArea));
    tableLookup.parmQuery(query);
    tableLookup.performFormLookup();
}

其实要做的工作和runbase框架差不多,也是要对dialogfield注册重载函数,SysOperation框架的处理方式是把这部分处理独立到单独的UI构建器,使得逻辑上比较清晰,注意postbuild方法中是如何得到交互对话框上的两个dialogfield控件的。SysOperation框架还支持使用自定义的form来构建UI,需要重载SysOperationController.templateForm()指定一个form。

showTextInInfolog()函数也从控制类中脱离出来被放到新的service类中:

class SysOpSampleSimpleService extends SysOperationServiceBase
{
}

public void showTextInInfolog(SysOpSampleSimpleDataContract data)
{
    if (xSession::isCLRSession())
    {
        info('Running in a CLR session.');
    }
    else
    {
        info('Running in an interpreter session.');
        if (isRunningOnServer())
        {
            info('Running on the AOS.');
        }
        else
        {
            info('Running on the Client.');
        }
    }


    info(strFmt('SysOpSampleSimpleService: %1, %2', data.parmNumber(), data.parmText()));
}

相应的,控制类的new()需要指定服务类的showTextInInfolog()为实际运行方法:

void new()
{
    super();

    this.parmClassName(classStr(SysOpSampleSimpleService));
    this.parmMethodName(methodStr(SysOpSampleSimpleService, showTextInInfolog));
}

这样做使得控制类变得更薄,整体上使用类来封装和组织分离各部分代码逻辑,也是为了使整体结构变得更加清晰。

第三个例子演示如何向通过控制类的parmExecutionMode()传入SysOperationExecutionMode枚举类型参数来实现不同的运行模式。主要关注的是Reliable Asynchronous方式,它等同于把操作放到batch server去运行,但不同点是在任务完成后从batch jobs列表中自动删除,仅保留运行历史纪录。这个例子稍显复杂,单靠文字不是很能说明清楚,这里就略过了。

最后一个例子更加详尽的演示了如何使用SysOperation做异步操作,包括如何在多个处理器上同时运行任务、监测异步操作错误、使用Alert机制提示等,从略省过。所以这里只是看看最简单的SysOperation是如何实现的,有兴趣的同学们好好看看这份白皮书吧。

原文地址:https://www.cnblogs.com/duanshuiliu/p/2684559.html