您的位置:首页 > 其它

[AX]AX2012 SSRS报表使用Report Data Provider Class作为数据集

2012-08-21 13:15 746 查看
在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$程序员还没来得及吧,程序员好累!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: