最新消息:ww12345678 的部落格重装上线,希望大家继续支持。

[转]关于更改当前公司(三)–一个奇怪的问题

Uncategorized William 2763浏览 0评论

在写代码的时候遇到一个非常奇怪的问题,感觉是AX类Application的setDefaultCompany和ChangeCompany方法冲突了。我要实现的功能很简单,从外部数据中读取数据,然后写到相关表中,因为外部数据表中包含多个公司的数据所以我要用到ChangeCompany,根据情况把数据插入到不同的公司中,如下所示:

while select area
        where area.isVirtual == NoYes::No &&
                area.id != 'DAT'
        {
            ChangeCompany(area.id)
            {

                 salesId = this.parmSQLProvider().insert_AXSalesTable(area.id);
                 this.parmSQLProvider().insert_AXSalesLine(area.id,salesId);

                 //Post
                 salesFormLetter = SalesFormLetter::construct(DocumentStatus::Invoice);
                 salesFormLetter.update(SalesTable::find(salesId));

            }
        }

其中调用的方法insert_AXSalesLine如下所示:

//Insert SalesLine
void insert_AXSalesLine(DataAreaId _areaId,SalesId _salesId)
{
    SalesLine       salesLine;
    SalesTable      salesTable;
    InventDim       inventDim;
    InventSite      site;
    str                sql;
    int             i;
    ;

    salesTable = null;
    salesTable = SalesTable::find(_salesId);

    while select SiteId from site
    {
        sql = this.sqlInsertCSP_SalesLine(site.SiteId);
        command = new CCADOCommand();
        command.activeConnection(connection);

        command.commandText(sql);
        recordSet = command.execute();
        comrs   =   recordSet.recordSet();

        while (!recordSet.EOF())
        {
            salesLine       = null;
            inventDim       = null;
            fields  = recordSet.fields();
            salesLine.initFromSalesTable(salesTable);
            //ItemId
            salesLine.ItemId = fields.itemIdx(0).value();
            salesLine.initFromInventTable(InventTable::findOrCreate(salesLine.ItemId));
            //Qty
            salesLine.SalesQty              = fields.itemIdx(1).value();
            salesLine.QtyOrdered            = fields.itemIdx(1).value();
            salesLine.RemainInventPhysical  = fields.itemIdx(1).value();
            salesLine.RemainSalesPhysical   = fields.itemIdx(1).value();

            //LineAmount
            salesLine.LineAmount            = fields.itemIdx(2).value();
            //InventDim;
            inventDim.InventSiteId          = site.SiteId;
            salesLine.InventDimId           = InventDim::findOrCreate(inventDim).inventDimId;
            salesLine.insert();

            comrs.moveNext();
        }
    }
}

由于外部的数据量比较大,所以while循环运行的时间比较长,不知道为什么这段程序偶尔会报错说,未指定销售订单号,并且提示公司切换到**公司,**公司正是当前打开的Client指定公司,然后退出。经过跟踪发现,在调用CCADO*相关类的方法时会触发类Application的setDefaultCompany方法,该方法把公司切换到了Client所指定的公司001,而实际上我的语句运行的语境应该在ChangeCompany方法指定的公司下,它帮我切换到了Client当前指定的公司,当然就会找不到相应的销售订单了,关键是干么要帮我调用Application的setDefaultCompany方法,这明显是不合适的,它这样一调用,把我的当前公司又给切换回去了,语境变了,后面的运行都是错的了。查MSDN也没找到一个具体在什么情况下会调用Application的setDefaultCompany方法的说明,只能自己一一排除了。经过反复测试,按如下说明可复现上述问题:

1.新建一个类,随便取什么名字,比如叫ChangeCompanyProblem之类的,默认情况下该类的RunOn属性为Called from

2.重载该类的new方法,不需要修改任何代码。

3.新建一个main方法,代码如下:

static void main(Args _args)
{
    ChangeCompanyProblem        problem;
    SalesTable                  salesTable;
    SalesLine                   salesLine;
    ;

    //Change to another Company that different from current one.
    ChangeCompany('001')
    {
        salesTable = null;
        salesLine  = null;
        select firstonly salesTable;

        //Just for having enough time to operating in the client;
        sleep(10000);
        problem = new ChangeCompanyProblem();

        select firstonly salesLine;
    }
}

这里的sleep方法,只是模拟正常情况下的一些耗时的计算,假设从select firstonly salesTable运行到problem = new ChangeCompanyProblem()需要10S,这个在一般的场景中应该是比较常见的。

4.新建一个MenuItem让其指向该类,默认情况下该MenuItem的RunOn属性为Client

5.在MainMenu中新建菜单项让其指向该MenuItem
6.在AX的标准菜单(实际上如果不通过AX的标准菜单项而是在AOT里直接打开菜单或者菜单项是不会出问题的)中打开该菜单项,在运行的时候点击客户端界面,随便点哪里,比如其他的菜单项之类的。
7.如果当前公司是”002″,应该会出现如下提示:

从上面的步骤可以看出,应该是在实例化类ChangeCompanyProblem的时候把当前公司从001切换到了002,在类ChangeCompanyProblem的new方法和类Application的setDefaultCompany里加断点跟踪也可以得出这样的结论。
将类ChangeCompanyProblem的Runon属性变成Server或者不重载类的new方法,再实例化的时候也就不会切换当前公司了,于是得出如下结论:

如果通过AX的标准菜单项调用一个类实现某个功能,在切换了公司的语境下,在该类的某个方法里要实例化其他类,而被实例化的类的new方法被重载了且用户在实例化该类之前在AX界面上有其他操作(比如随便点击界面的某个地方),如果该类被实例化时发生在客户端,将会触发类Application的setDefaultCompany,导致当前公司切换至当前Client所指定的公司,这样该类被实例化后,所有的代码执行的语境将会被更改至Client的当前公司而不是ChangeCompany所指定的公司,会产生与预期不同的结果。貌似很绕口的说。。。
所以需要注意以下两点:
1.如果要在ChangeCompany包括的语句中实例化类,一定要让这些类运行在服务器端;
2.避免在ChangeCompany包括的语句中调用info,print之类的向客户端打印信息的方法,因为这也会导致application的stDefaultCompany的调用。

由于以上只是出于自己的简单测试,没有见过相应的权威的解释,没办法从原理上得到合理的解释,只是通过有限的测试得到的结果,不能保证正确性,还望高手指点。

测试版本 AX2009 SP1(RU3)

关于更改当前公司(一)–ChangeCompany

关于更改当前公司(二)–ChangeCompany

关于更改当前公司(三)–一个奇怪的问题

转载请注明:ww12345678 的部落格 | AX Helper » [转]关于更改当前公司(三)–一个奇怪的问题

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址