zoukankan      html  css  js  c++  java
  • [AX]AX2012 SSRS报表使用Report Data Provider Class作为数据集

    在SSRS报表中使用默认的Dynamics AX作为数据源时可以使用多种数据集抓取数据,Report data provider class是其中一种,它用在一些数据在呈现到报表前还需要做一些处理,这些处理在AX中使用X++完成。

    如果要在报表中使用一些参数,就需要在AOT中定义一个DataContract类,由它来定义报表所有参数名称及类型:

    [DataContractAttribute]
    public class SrsRDPContractSample
    {
        AccountNum accountNum;
        CustAccountStatement accountStmt;
        boolean inclTax;
    }
    
    
    [DataMemberAttribute("AccountNum")]
    public AccountNum parmAccountNum(AccountNum _accountNum = accountNum)
    {
        accountNum = _accountNum;
        return accountNum;
    }
    
    
    [DataMemberAttribute("CustAccountStatement")]
    public CustAccountStatement parmAccountStmt(CustAccountStatement _accountStmt = accountStmt)
    {
        accountStmt = _accountStmt;
        return accountStmt;
    }
    
    [DataMemberAttribute("InclTax")]
    public boolean parmInclTax(boolean _inclTax = inclTax)
    {
        inclTax = _inclTax;
        return inclTax;
    }

    SrsRDPContractSample是一个不继承于任何类的类,通过DataContractAttribute特性标注,定义了几个parmXXX方法来返回成员变量,这些方法通过DataMemberAttribute特性来定义报表参数的名称。

    [
        SRSReportQueryAttribute (querystr(Cust)),
        SRSReportParameterAttribute(classstr(SrsRDPContractSample))
    ]
    public class SrsRdpSampleClass extends SRSReportDataProviderBase
    {
        TmpCustTableSample tmpCust;
    }
    
    
    [SRSReportDataSetAttribute("TmpCust")]
    public TmpCustTableSample getTmpCustTable()
    {
        select * from tmpCust;
        return tmpCust;
    }
    
    
    public void processReport()
    {
        AccountNum              accountNumber;
        CustAccountStatement    custAcctStmt;
        boolean                 boolInclTax;
        Query                   query;
        QueryRun                queryRun;
        QueryBuildDataSource    queryBuildDataSource;
        QueryBuildRange         queryBuildRange;
        CustTable               queryCustTable;
    
        SrsRdpContractSample    dataContract;
    
        // Get the query from the runtime using a dynamic query. 
        // This base class method reads the query specified in the SRSReportQueryAttribute attribute.
        query = this.parmQuery();
            
        // Get the parameters passed from runtime. 
        // The base class methods read the SRSReportParameterAttribute attribute. 
        dataContract = this.parmDataContract();
        accountNumber = dataContract.parmAccountNum();
        custAcctStmt = dataContract.parmAccountStmt();
        boolInclTax = dataContract.parmInclTax();
            
        // Add parameters to the query.
        queryBuildDataSource = query.dataSourceTable(tablenum(CustTable));
            
            
        if(accountNumber)
        {
            queryBuildRange = queryBuildDataSource.findRange(fieldnum(CustTable, AccountNum));
            if (!queryBuildRange)
            {
                queryBuildRange = queryBuildDataSource.addRange(fieldnum(CustTable, AccountNum));
            }
            // If an account number has not been set, then use the parameter value to set it.
            if(!queryBuildRange.value())
                queryBuildRange.value(accountNumber);
        }        
            
        if(custAcctStmt)
        {
            queryBuildRange = queryBuildDataSource.findRange(fieldnum(CustTable, AccountStatement));
            if (!queryBuildRange)
            {
                queryBuildRange = queryBuildDataSource.addRange(fieldnum(CustTable, AccountStatement));
            }
            // If an account statement has not been set, then use the parameter value to set it.
            if(!queryBuildRange.value())
                queryBuildRange.value(int2str(custAcctStmt));
        }
            
        if(boolInclTax)
        {
            queryBuildRange = queryBuildDataSource.findRange(fieldnum(CustTable, InclTax));
            if (!queryBuildRange)
            {
                queryBuildRange = queryBuildDataSource.addRange(fieldnum(CustTable, InclTax));
            }
            // If flag to include tax has not been set, then use the parameter value to set it.
            if(!queryBuildRange.value())
                queryBuildRange.value(int2str(boolInclTax));
        }        
            
        // Run the query with modified ranges.
        queryRun = new QueryRun(query);
        ttsbegin;
        while(queryRun.next())
        {
            tmpCust.clear();
            queryCustTable = queryRun.get(tablenum(CustTable));
            tmpCust.AccountNum = queryCustTable.AccountNum;
            tmpCust.CustName = queryCustTable.name();
            tmpCust.LogisticsAddressing = queryCustTable.address();
            tmpCust.CustGroupId = queryCustTable.CustGroup;
            tmpCust.Phone = queryCustTable.phone();
            tmpCust.CustInvoiceAccount = queryCustTable.InvoiceAccount;
            tmpCust.CustAccountStatement = queryCustTable.AccountStatement;
            tmpCust.InclTax = queryCustTable.InclTax;
            tmpCust.insert();
    
        }
        ttscommit;
    }

    SrsRdpSampleClass就是我们的Report data provider class,继承于SRSReportDataProviderBase类,注意特性SRSReportQueryAttribute('Cust')标识了所使用的Query对象,特性SRSReportParameterAttribute(classstr(SrsRDPContractSample))则说明了前面定义的报表参数data contract类。SrsRdpSampleClass定义了表TmpCustTableSample类型的变量tmpCust,TmpCustTableSample是一个TmpDB类型的临时表,通过getTmpCustTable方法返回这个临时表变量中的纪录数据,注意getTmpCustTable方法带有特性[SRSReportDataSetAttribute('TmpCust')],标识一个名为TmpCust的数据集。那么这个临时表tmpCust中的纪录数据又是哪里来的呢?这个是在重载函数processReport()函数中添加的,先是从通过this.parmQuery()得到相应的query对象,this.parmDataContract()得到data contract对象,根据参数来添加过滤条件到Query,最后运行Query,得到的结果添加到临时表变量tmpCust。

    在VS2010中要使用这个report data provider,需要把data source type设为Report data provider:

    在属性Query中点击...按钮可以选择到SrsRdpSampleClass及其fields,结果自动写入到Query属性中的查询字符串。有了dataset就可以布局数据字段了,更详细的步骤参看http://technet.microsoft.com/en-us/library/gg724119.aspx。注意Dataset的Dynamic Filters属性,设为true允许用户在query中自定义一些过滤的条件。

    Report data provider被.net编写的service调用,所以它的调试需要做一些特别的设置。首先要保证AOS服务所用账号被添加到Microsoft Dynamics Ax Debugging Users本地用户组中,同时Microsoft Dynamics AX server cofiguration中启用Enable brakpoints to debug X++ code running on this server和Enable global breakpoints to debug X++ code running in batch jobs,当然用户options中Debug mode 设置为when breakpoint。最后还要记得手工打开运行Microsoft dynamics 2012 debugger,只有debugger窗口打开时才会中断到断点。详见http://msdn.microsoft.com/en-us/library/gg724081.aspx

    遇到一个奇怪的问题是如果在Report data provider class中更改了所用的Query,比如把前面的SRSReportQueryAttribute('Cust')改成SRSReportQueryAttribute('CopyOfCust'),CopyOfCust是从Query “Cust”复制过来的,在VS2010中预览报表可以看到结果是来自于这个新的Query,但是如果在AX中已经使用过MenuItem运行过这个报表就会发现总是使用第一次运行时所用的Query,原以为这和用户的User Data有关系,清理了几遍User data也是这样。被这个问题折腾了快半天,过调试代码发现整个过程异常复杂,大致是SrsReportRunService.getReportDataContract(str _reportName)初始化SrsReportDataContract的实例->SrsReportRunService.getRdlParser(str _reportName)->SrsReportRunCache.getRdlParser(str _reportName)->SrsReportRunCache::getValue(SrsReportRunCacheScope _scope, container _key)->classfactory.globalObjectCache().find(SrsReportRunCache::getCacheScopeStr(_scope), _key),最后看到这些信息pack之后被存放到了SysGlobalObjectCache,而不是User data中,所以清理User data没有用。我们知道SysGlobalObjectCache存放的是在所有client session可以共享的缓存数据,用来存放那些频繁读取的数据以提高性能,重启AOS这些cache数据就丢失了,而我们的这个问题实际上是可以手工刷新来解决的:Tools->Caches->Refresh elements,为什么就不能在VS2010更新报表后自动刷新下相关缓存纪录呢?!M$程序员还没来得及吧,程序员好累!

  • 相关阅读:
    【算法学习笔记】76.DFS 回溯检测 SJTU OJ 1229 mine
    【算法学习笔记】75. 动态规划 棋盘型 期望计算 1390 畅畅的牙签盒(改)
    【算法学习笔记】74. 枚举 状态压缩 填充方案 SJTU OJ 1391 畅畅的牙签袋(改)
    【算法学习笔记】73.数学规律题 SJTU OJ 1058 小M的机器人
    【算法学习笔记】72.LCS 最大公公子序列 动态规划 SJTU OJ 1065 小M的生物实验1
    【算法学习笔记】71.动态规划 双重条件 SJTU OJ 1124 我把助教团的平均智商拉低了
    【算法学习笔记】70.回文序列 动态规划 SJTU OJ 1066 小M家的牛们
    【算法学习笔记】69. 枚举法 字典序处理 SJTU OJ 1047 The Clocks
    【算法学习笔记】68.枚举 SJTU OJ 1272 写数游戏
    【算法学习笔记】67.状态压缩 DP SJTU OJ 1383 畅畅的牙签袋
  • 原文地址:https://www.cnblogs.com/duanshuiliu/p/2648819.html
Copyright © 2011-2022 走看看