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$程序员还没来得及吧,程序员好累!

  • 相关阅读:
    Python——pygame入门实例
    Python——基础函数2
    Python——面向对象的三特征
    201521123083《Java程序设计》第四周学习总结
    201521123083《Java程序设计》第二周学习总结
    201521123083 《Java程序设计》第6周学习总结
    《Java程序设计》第1周学习总结
    201521123083《Java程序设计》第5周学习总结
    《Java程序设计》第3周学习总结
    201521123083 《Java程序设计》第7周学习总结
  • 原文地址:https://www.cnblogs.com/duanshuiliu/p/2648819.html
Copyright © 2011-2022 走看看