65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 SOAP 调用将 XML 传递给数据扩展的报告服务

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (5投票s)

2005年10月19日

CPOL

8分钟阅读

viewsIcon

86444

downloadIcon

427

演示了如何通过 SOAP 调用将 XML 传递给数据扩展来呈现报表。

引言

此示例使用 ASP.NET 筛选器页面构造 XML,然后将其作为参数传递给报表服务 Web 服务调用以呈现报表。XL 参数被一个注册在报表服务 Web 服务下的自定义数据扩展 DLL 拦截。自定义数据扩展解析 XML,并决定调用哪个业务组件 Web 服务方法来获取用于报表处理的数据集。业务组件 Web 服务充当数据访问层 (DAL),它也接受 XML 作为参数,并解析要添加到 SQL Server 下执行的查询中的筛选器,然后将结果集作为数据集返回。

此示例不创建安全扩展,而是使用内置的 Windows 安全性;因此,ASPX 代码具有 Windows 身份验证并设置线程标识进行模拟。自定义数据扩展将用作报表定义语言的数据源,包括设计时和运行时。

部署数据扩展

通过向配置文件添加条目来部署数据扩展。需要修改四个配置文件才能注册自定义数据扩展。在修改这些文件之前,请备份以防修改出错。恢复原始配置文件将使报表服务恢复功能。

C:\Program Files\Microsoft SQL Server\80\Tools\Report Designer\RSReportDesigner.config:

<Data>
   <Extension Name="CUSTOM" 
      type="CustomDataExtension.DataSetConnection, 
            CustomDataExtension"/>
</Data>

C:\Program File\Microsoft SQL Server\80\Tools\Report Designer\rspreviewpolicy.config

<CodeGroup class="UnionCodeGroup" version="1" 
          PermissionSetName="FullTrust" 
          Name="DataExtensionSample" 
          Description="Code group for sample data processing extension">
  <IMembershipCondition class="UrlMembershipCondition" 
          version="1"
          Url="C:\Program Files\Microsoft SQL Server\
              80\Tools\Report Designer\CustomDataExtension.dll"/>
</CodeGroup>

C:\Program Files\Microsoft SQL Server\MSSQL\Reporting Services\ReportServer\RSReportServer.config

<Data>
   <Extension Name="CUSTOM" 
     type="CustomDataExtension.DataSetConnection, 
           CustomDataExtension"/>
</Data>

筛选器页面

筛选器页面使用 ASPX 网页进行构建。在页面提交时,XML 在代码隐藏中构造,并作为报表参数传递给 SOAP 调用,然后这些调用传递给数据扩展接口。此 XML 以命令文本的形式传递到数据扩展。通过这种方式,数据扩展可以解析 XML 并将 Web 方法调用追加到 XML 中,然后将其传递给业务 Web 服务方法。

系统流程

筛选器页面 --(提交)--> Web 服务器 --(XML)--> 报表服务 WS --(XML) -- > 业务 WS --(XML)--> 数据访问层

命令文本是 XML 传递到数据扩展的唯一方式。选择 XML 作为传递参数的手段是因为 XML 可以非常大并且包含大量元素。报表定义语言 (RDL) 文件包含一个报表参数部分,而数据集部分包含命令文本。通过在 Visual Studio 中打开 RDL 文件并选择“查看代码”选项,手动修改 RDL 文件,添加一个名为 XmlCommandText 的报表参数,并按如下方式修改数据集部分的命令文本:

Report1.rdl

<ReportParameters>
  <ReportParameter Name="XmlCommandText">
   <DataType>String</DataType>
   <Prompt>CommandText</Prompt>
  </ReportParameter>
</ReportParameters>

注意:可以在上述报表参数中添加默认 XML 值,以便在预览模式下测试报表。

数据集部分

<Query>
  <DataSourceName>CustomDataExtension</DataSourceName>
  <CommandText>=Parameters!XmlCommandText.Value</CommandText>
  <QueryParameters>
   <QueryParameter Name="WebMethod">
    <Value>GetCustomers</Value>
   </QueryParameter>
  </QueryParameters>
</Query>

当调用数据扩展接口时,CommandText 将接收 XML 值。XML 值作为报表参数值传递给 SOAP。

WebForm1.aspx.cs

ReportServer.ReportingService rs = new ReportServer.ReportingService();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
... 
ParameterValue[] parameters = new ParameterValue[1];
parameters[0] = new ParameterValue();
parameters[0].Name = "XmlCommandText";
parameters[0].Value = doc.InnerXml;
... 
result = rs.Render("/RSTest/Report1", "HTML4.0", historyID, 
                   devInfo, parameters, credentials, showHideToggle, out encoding, 
                   out mimeType, out reportHistoryParameters, 
                   out warnings, out streamIDs);
Response.BinaryWrite(result);

XML 仅包含要传递给数据扩展的筛选器参数。Web 服务方法名称存储在数据集参数中,后面将讨论。

XML 示例

<doc>
  <Parameters>
   <CustomerID>12345</CustomerID>
   <SalesOrderNumber>s00125</SalesOrderNumber>
  </Parameters>
</doc> 

数据源

报表 RDL 中的数据源设置为使用自定义数据扩展。自定义数据扩展同时在报表设计器配置文件和报表服务器配置文件中注册。一旦自定义数据扩展在设计器中注册,它就会出现在 Visual Studio .NET 报表设计视图中。然后从数据集选项卡中选择自定义数据扩展。

Report1.rdl

<DataSources>
  <DataSource Name="CustomDataExtension">
  <rd:DataSourceID>08b67ea6-e6c2-4b64-bcc4-650dc06c73e6</rd:DataSourceID>
  <ConnectionProperties>
   <DataProvider>CUSTOM</DataProvider>
   <ConnectString/>
  </ConnectionProperties>
  </DataSource>
</DataSources>

报表 RDL 中的 Datasources 部分包含自定义数据扩展的引用。

数据扩展

在报表服务处理期间,数据扩展会为报表 RDL 中的每个数据集调用。每个数据集都绑定到一个数据源,并且每个数据集包含一个查询参数,该参数将被添加到数据扩展参数集合实现中。

数据集和数据扩展协同工作

报表 RDL 包含一个数据集部分,用于描述数据字段和查询。数据集部分可能包含一个或多个数据集,具体取决于报表布局。每个数据集条目都有一个查询部分,其中包含命令文本条目和查询参数。在此示例中,我将每个数据集映射到一个 Web 服务方法调用,该调用在运行时将执行业务数据访问层以检索数据集。我添加了一个名为“WebMethod”的查询参数,并将方法名称作为参数值。此参数被追加到数据扩展中的命令文本 XML。

Report1.rdl

<Query>
  <DataSourceName>CustomDataExtension</DataSourceName>
  <CommandText>=Parameters!XmlCommandText.Value</CommandText>
  <QueryParameters>
   <QueryParameter Name="WebMethod">
    <Value>GetCustomers</Value>
   </QueryParameter>
  </QueryParameters>
</Query>

DataSetCommand.cs

public IDataReader ExecuteReader(CommandBehavior behavior)
{
  XmlDocument doc = new XmlDocument();
  try
  {
   // load the CommandText XML and append dataset parameters

   doc.LoadXml(CommandText);
   // Append all the dataset parameter

   // example: WebMethod contains which web service method to execute.

   DataSetParameter parameter = null;
   IEnumerator enumerator = _parameters.GetEnumerator();
   while (enumerator.MoveNext())
   {
    parameter = (DataSetParameter) enumerator.Current;
    XmlElement elem = doc.CreateElement(parameter.ParameterName);
    elem.AppendChild(doc.CreateTextNode(parameter.Value.ToString()));
    doc.DocumentElement.AppendChild(elem);
   }
  }
  catch (Exception ex)
  {
   throw new Exception("Invalid Xml command Text: " + CommandText, ex);
  }
  return new DataSetDataReader(doc.InnerXml);
}

报表服务在渲染执行期间调用参数集合对象以添加数据集查询参数。数据扩展 IDbCommand.ExecuteReader 方法将 CommandText 值加载为 XML,迭代所有查询参数,并将名称-值对作为 XML 元素追加到主 XML 中。主 XML 包含来自筛选器页面的筛选器参数元素。然后将结果 XML 输入到 DataSetDataReader 接口。DataReader 构造函数随后调用业务 Web 服务方法,并将 XML 传递给数据访问层。

我可以用同样的方式处理子报表的 XML 参数传递。子报表 XML 参数必须在主报表 RDL 文件中硬编码。例如:

导航选项卡 - 跳转到报表:销售订单明细

参数名称 参数值
XmlCommandText "<doc><Parameters><SalesOrderNumber>" & Fields!SalesOrderNumber.Value & "</SalesOrderNumber></Parameters></doc>"

RDL 操作部分包含带有 XML 参数和值的子报表条目。同样,这里的参数名称是“XmlCommandText”。此报表参数也在子报表 RDL 中创建。在整个过程中,XML 是四处传递的元数据。

Report1.rdl

<Action>
  <Drillthrough>
   <Parameters>
    <Parameter Name="XmlCommandText">
    <Value>="&lt;doc&gt;&lt;Parameters&gt;&lt;SalesOrderNumber&gt;" & 
        Fields!SalesOrderNumber.Value & 
        "&lt;/SalesOrderNumber&gt;&lt;/Parameters&gt;&lt;/doc&gt;"
    </Value>
    </Parameter>
   </Parameters>
   <ReportName>Sales Order Detail</ReportName>
  </Drillthrough>
</Action>

数据扩展

数据扩展通过命令文本处理 XML 参数传递,并将数据集查询参数追加到 XML 和调用的业务 Web 服务方法中。XML 被传递给业务 Web 服务调用。此数据扩展原型仅调用单个 Web 服务方法,并将 XML 传递给 Web 服务方法。然后从 XML 参数中解析调用方法,并使用调用方法名称对访问层进行相应的调用。

IDataReader::DataSetDataReader

public DataSetDataReader(string sXml)
{
  try
  {
   BusinessService.BusinessData BusWS = new BusinessService.BusinessData();
   this._dataSet = BusWS.GetReportDataSet(sXml);
   //set the current row to one before the first

   _currentRow = -1;
  }
  catch(Exception ex)
  {
   throw new Exception("Error calling web service XML: " + sXml, ex);
  }
}

DataSetDataReader 构造函数调用业务 Web 服务方法来获取数据集。数据集由业务 Web 服务方法中的访问层生成。XML 是从筛选器页面传递到报表服务再到数据扩展接口的相同 XML,现在它正被传递到业务 Web 服务方法。一次 Web 服务调用最大限度地减少了对数据扩展 DLL 的更改。XML 参数非常灵活,可以包含任意数量的元素,并且可以在不进行根本性接口更改的情况下进行传递。

业务 Web 服务

在此层,可以将 XML 传递给数据访问层进行处理,数据访问层也可以将 XML 直接传递给存储过程。我这里的实现很简单;我解析 XML,找出要调用数据访问层中的哪个方法,然后传递 XML。

BusinessData.asmx

[WebMethod]
public DataSet GetReportDataSet(string sXml)
{
  // parse xml
  // call business component based on method defined in xml

  DataSet ds = null;
  try
  {
   string sWebMethod = string.Empty;
   XmlDocument doc = new XmlDocument();
   doc.LoadXml(sXml);
   sWebMethod = doc.SelectSingleNode("//WebMethod").InnerText;
   BusinessDataAccess DataAccess = new BusinessDataAccess();
   switch (sWebMethod)
   {
    case "GetCustomers":
     ds = DataAccess.GetCustomers(sXml);
     break;
    case "GetTerritorySales":
     ds = DataAccess.GetTerritorySales(sXml);
     break;
    case "GetSalesOrder":
     ds = DataAccess.GetSalesOrder(sXml);
     break;
    case "GetSalesOrderDetail":
     ds = DataAccess.GetSalesOrderDetail(sXml);
     break;
   }
  }
  catch
  {
  }
  return ds;
}

业务 Web 服务方法解析 XML,确定要调用哪个数据访问方法,然后通过传递 XML 作为参数来执行调用。数据访问方法然后解析 XML 以检索筛选器参数,并执行 SQL 以检索结果作为数据集。在这里,我们可以选择不解析 XML,只需调用数据访问层中的一个方法,让数据访问层进行解析并执行相应的数据检索方法并返回数据集。选择取决于开发人员,但这种方法的关键优势在于处理 XML 和返回数据集结果的灵活性。

问题

图像链接必须通过 SOAP 调用单独处理。使用 SOAP API 调用时,图像链接会中断。自定义报表查看器通过将流下载到本地文件夹来处理图像。

使用此方法通过 SOAP 调用处理纯净报表非常简单,但交互式报表(例如带有钻取功能的报表)会带来很大的问题。钻取报表链接基于 URL,点击后会将整个页面变成 URL 风格页面。这并不好。我希望微软能齐心协力,让报表服务更具 ASP.NET 兼容性,使用 post back 机制而不是基于 URL 的方式来处理运行在 ASP.NET 网页上的报表。

实现这一点的一种方法是编写一个呈现扩展,将所有链接替换为 post back JavaScript 函数和一个自定义报表查看器控件,该控件可以处理所有 post back 点击并发出 SOAP 调用以检索报表片段,同时,为该过程添加报表会话缓存管理。这样,报表服务将成为一个完整的 ASP.NET 中心产品。

© . All rights reserved.