从 SSRS(RDL)服务器报告中调用 WCF 服务






4.92/5 (14投票s)
一篇说明如何从 SSRS 服务器报告中调用 WCF 服务的文章

引言
Microsoft SQL Services Reporting Services 是一种与各种数据源集成的报告工具。它允许直接查询 XML 数据源和 Web 服务。这通过使用 XML 数据提供程序实现,该提供程序将 XML 结构展平为易于报告引擎使用的数据集。我需要从报告中使用 WCF 服务,而找到指导我正确方向的资源并非易事。
背景
关于如何实现此目的的资源稀缺,有时甚至相当肤浅,在我的探索结束后,我认为撰写一篇可能帮助其他人的文章是公平的。我必须承认,我在此领域还不是权威,我做出的一些假设/断言可能并非绝对。为了说明如何从服务器报告中使用 WCF 服务,我将引导您完成一个简单的练习。
Using the Code
解压源代码后,您应该会得到一个包含三个项目的 VS 解决方案:WcfSSRSEx1
、WcfHost
和 RptEx1
。接下来您可能需要做的是配置一个名为 WcfHost 的虚拟目录。让该虚拟目录的物理路径指向之前解压的 WcfHost
网站项目的根目录。之后,打开解决方案并重建它。在将报告部署到报告服务器之前,请确保项目具有正确的 TargetReportFolder
和 TargetServerURL
(右键单击项目,然后单击“属性”)。右键单击 RptEx1
项目,然后单击“部署”。然后您可以通过浏览 WcfHost
网站项目中的 Default.aspx 页面来查看报告。
我将带您了解从服务器报告中使用 WCF 服务的过程。
我们首先创建一个 WCF 服务库。您将在 VS 2008 的 WCF 项目类型下找到一个 WCF 服务库模板。我发现的第一件事是,大多数关于这个主题的作者都假设典型开发人员的需求是在 Web 项目中使用 WCF 服务。
WCF 服务库(命名为 WcfSSRSEx1
)包含:
DataContract
:Book
[DataContract] public class Book { [DataMember] public String ISBN { get; set; } [DataMember] public String Title { get; set; } [DataMember] public String Author { get; set; } [DataMember] public Decimal Price { get; set; } [DataMember] public Boolean IsAvailable { get; set; } }
ServiceContract
:ILibraryService
及其实现LibraryService
[ServiceContract(Namespace="https:///2009/libraryservice")] public interface ILibraryService { }
OperationContract
:FetchBooks
,它接受两个参数:一个String
类型的参数Author
和一个Boolean
类型的参数IsAvailable
,并返回一个包含相应作者和可用状态的书籍列表。
这里需要说明几点。
报告引擎通常会向 Web 服务发送 SOAP 请求。为了使报告引擎能够调用操作,您只能使用一个参数。该参数的类型必须使用 MessageContract
属性进行修饰。单个参数包装了使用 MessageBodyMember
属性修饰的各个参数。这是使 WCF 服务库操作可供服务器报告使用的第一步。
我们需要做的下一件事是通过 basicHttpBinding
将 WCF 服务公开为传统 Web 服务。对于本练习,我们使用 IIS 作为 WCF 服务的主机。我相信可以使用 Windows 服务甚至简单的控制台项目来做同样的事情。我们创建主机网站(命名为 WcfHost
)。问题是,当从报告中使用 Web 服务时,您通常会创建一个类型为“XML”的共享数据源,并将服务路径指定为连接字符串。
要公开服务,请在 WcfHost
项目中添加一个新的 WCF 服务类。为方便起见,您可以将其命名为 LibraryService
。VS 会尽职地为您添加三个文件:App_Code 文件夹中的 ILibraryService.cs 和 LibraryService.cs,以及根项目文件夹中的 LibraryService.svc(即,如果您将 WCF 服务类添加到根项目文件夹中)。删除添加到 App_Code 文件夹的两个文件(ILibraryService.cs 和 LibraryService.cs)。我们这样做是因为我们的服务已经在 WCF 服务库中实现。
此时,您需要将我们的 WCF 服务库项目(WcfSSRSEx1
)的引用(从解决方案资源管理器中添加引用)添加到网站项目中。打开 LibraryService.svc 文件。编辑唯一的一行
<%@ ServiceHost Language="C#" Debug="true"
Service="TestService" CodeBehind="~/App_Code/TestService.cs" %>
使其与此对应
<%@ ServiceHost Service="WcfSSRSEx1.LibraryService" %>
下一步是在 web.config 中配置 WCF 服务本身。在添加 LibraryService
WCF 服务类时,可能会添加以下行
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="LibraryServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="LibraryServiceBehavior"
name="LibraryService">
<endpoint address=""
binding="basicHttpBinding"
contract="ILibraryService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
您需要再次编辑它(或添加此部分),使其与此对应
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="WcfSSRSEx1.LibraryServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="WcfSSRSEx1.LibraryServiceBehavior"
name="WcfSSRSEx1.LibraryService">
<endpoint address=""
binding="basicHttpBinding"
contract="WcfSSRSEx1.ILibraryService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
确保至少有一个 LibraryService
端点使用 httpBasicBinding
。无需解释,我们在此处所做的其他更改显然是因为我们的服务是在引用的库中实现的。
此时,您可以在浏览器中输入 https:///WcfHost/LibraryService.svc 来浏览服务。或者,您可以右键单击 LibraryService.svc 并单击“在浏览器中查看”。您甚至可以通过输入 https:///WcfHost/LibraryService.svc?wsdl 来查看 wsdl。
我们的服务现在已准备好供服务器报告使用。
这里需要提几件事。
首先,通过安装带有高级服务的 SQL Express,您确实会安装 ReportServer
和 ReportServer
数据库,但该版本的 ReportServer
是有限的。它不允许服务器报告使用 XML 数据源。
其次,如果您使用 VS 2008 设计报告,您将无法将其部署到 2005 ReportServer
,反之亦然。微软已在互联网上充分记录了这些原因。
第三,配置 ReportServer
并非易事。我建议您在安装 SQL Server 时以本机模式安装 ReportServer
。然而,这并不能保证您一帆风顺。您可能需要解决一些问题,我可能不会在本文中涵盖,但我必须提到,最令人恼火的是,当我在计算机上启用用户访问控制时,尽管我是计算机管理员并以管理员身份运行 IE – “以管理员身份运行” – ReportManager
却给了我一个剥夺了其主要功能的首页。当我禁用 UAC,重新启动计算机并再次访问它时,该页面才具有所有功能。
回到正题,添加一个新的报表服务器项目(在商业智能项目模板下 – 命名为 RptEx1
)。您可能需要做的第一件事是配置项目的 TargetReportFolder
和 TargetServerURL
。右键单击项目并适当设置它们。

添加一个新的共享数据源,给它一个适当的名称(LibraryDS
对我来说可行),指定 XML 作为类型,并将 https:///WcfHost/LibraryService.svc 设置为连接字符串。

您可以通过多种方式执行下一步。您可以单独添加报告和数据集(从 ReportData
窗口),然后将字段从 DataSet
拖到报告上,或者您可以使用报告向导,该向导在您右键单击“报告”文件夹,然后从解决方案资源管理器中单击“添加新报告”后获得。使用向导,在向导的第二步中选择 LibraryDS
作为数据源。第三步将允许您输入查询。这一步需要您最多的注意力,因为任何小错误都可能导致失败。
发出 soap 请求时的查询形式如下:
<Query>
<Method Name="BookRequest" Namespace="https:///2009/libraryservice">
<Parameters>
<Parameter Name="Author"></Parameter>
<Parameter Name="IsAvailable"></Parameter>
</Parameters>
</Method>
<SoapAction>
https:///2009/libraryservice/ILibraryService/FetchBooks
</SoapAction>
</Query>
如果您熟悉我们的 Web 服务 wsdl,则命名空间和 SoapAction
将不言自明。参数也映射到我们 WCF 服务中由 MessageContract
装饰的类中包装的参数。
在下一步中,您可以指定报告类型(表格和矩阵),而第五步允许您设计表格。这是向导可能不太智能的一个步骤。向导可能会将可用字段列为 xmlns、a、i 以及 SOAP 响应中包含的任何对象。对于我们的情况,我们有 Books
– FetchBooks
OperationContract
返回一个名为 Books
的包装 List<Book>
对象。但这只说对了一半。您可以在报告中访问实际对象的 public
属性。您可以安全地忽略此步骤,稍后将字段拖到报告中。下一步允许您选择表格样式,而最后一步允许您指定报告的名称。
如果您使用了向导,您将在报表数据窗口(视图->报表数据菜单项)中得到一个报表和一个数据集(DataSet1
或等效项)。
您可以删除可能已添加到报告中的任何 Tablix,因为它很可能不会给出您想要的结果。要公开查询返回对象的属性,请右键单击 dataset
并单击 DataSet
属性。单击左侧的“字段”选项卡,并根据您对返回对象的了解添加适当的查询字段。添加适当的字段后,您可以继续添加一个新的 Tablix 并将字段从 DataSet
拖到其上。

从“报表数据”窗口添加两个参数,方便起见,命名为 Author
和 IsAvailable
(您也可以添加一些适当的提示文本)。右键单击数据集,然后单击 DataSet
属性。单击左侧的“参数”选项卡,并添加两个参数,Author
和 IsAvailable
——这次名称必须与查询中的名称匹配——并将它们的值分别设置为从“报表数据”窗口添加的参数(@Author
和 @IsAvailable
)。

此时,您可以松一口气了,因为如果您在“报表设计器”窗口中单击“预览”选项卡,并输入提示值(作者姓名为 Richard Price,可用状态为 true——如果您使用我的示例),然后单击“查看报表”按钮,您应该会在网格中得到一些不错的结果。我很想说,就这么简单,但第一次做的时候可不简单。
我们从服务器报告中使用 WCF 服务的目标已实现,但其他步骤将涉及从网页中使用已部署的报告,同时传递适当的参数。部署服务器报告是一个简单的问题。右键单击项目并单击“部署”,如果您的 TargetServerURL
和 TargetReportFolder
设置正确,操作将成功。以下是您如何从网页的代码后台引用报告的方法。
ReportViewer1.ProcessingMode = ProcessingMode.Remote;
ReportViewer1.ServerReport.ReportServerUrl =
new Uri("https:///ReportServer/");
ReportViewer1.ServerReport.ReportPath = "/Reports/RptEx1/BookReport";
ReportParameter[] rptParameters = new ReportParameter[2];
rptParameters[0] = new ReportParameter("Author", "Richard Price");
rptParameters[1] = new ReportParameter("IsAvailable", "true");
//Or ("IsAvailable", "1")
ReportViewer1.ServerReport.SetParameters(rptParameters);
有时,IIS 中报表相应处理程序的映射可能未发生,您可能会遇到这个难看的网页错误

只需前往 IIS,点击“默认网站”,然后点击“处理程序映射”(在“服务器组件”下),添加“托管处理程序”
- 请求路径:Reserved.ReportViewerWebControl.axd
- 类型:
Microsoft.Reporting.WebForms.HttpHandler
- 名称:Reserved-ReportViewerWebControl-axd
然后您就可以愉快地查看一个使用 WCF 服务的精美服务器报表了。
关注点
首先,即使使用 MessageContract
修饰的类来包装 WCF 操作参数,如果您创建客户端(例如使用 svcutil.exe 或 VS – 添加服务引用),生成的客户端仍会要求您单独提供操作参数。
假设您添加了一个名为 LibService
的服务引用;
LibService.LibraryServiceClient libService =
new LibService.LibraryServiceClient();
Book[] books = libService.FetchBooks("Richard Price", true);
就好像您有一个操作 FetchBooks(String Author, Boolean IsAvailable)
,这是一个您的服务实际上没有的签名。同样适用于返回类型。您不会收到 BookResponse
类型的对象,而是收到 List<Book>
类型的对象(实际上是 Book[]
)。这可能是一件好事,因为在典型情况下调用 WCF 操作时,您不必为 MessageContract
修饰的参数而烦恼。
其次,我不知道报表查询响应以获取目标字段(如果操作返回对象具有类型和泛型列表作为可公开访问的属性/字段)有多容易(或不可能)。有人知道 XPath
吗?