通过WebService来使用报表

WebService由于其中的数据经过了XML文档打包,因此其实际传送的数据量大于实际的信息量,而且使用了HTTP这种比较低效的连接方式,因此其运行速度比较慢。但它是国际标准,是跨平台交换数据的好方法,因此得到大大小小的软件厂商的支持,正在普及应用。

相信有很多应用系统,基本结构是C/S系统,但此时应用系统不再直接连接数据库了,而是使用WebService从一个Web服务器上下载DataSet来访问数据。这样有诸多好处,
1。安全,客户端不用存放数据库连接字符串这样的敏感信息了。
2。其次是部署方便,不用在客户端安装各种数据库客户端API,不要进行各种配置了。
3。优化数据库的访问,此时只有服务器程序直接连接数据库,客户端不连接数据库,因此服务器程序可以放心的针对数据库进行优化。
4。可扩展性好,这种系统很多修改只限于服务器程序,而且比较容易转换为纯B/S系统。

有鉴于此,在下也赶赶时髦,想让我的报表工具也支持这种WebService方式。

这种WebService的原理我设计的很简单,其过程如下
1。报表设计器或运行在客户端的报表引擎加载报表模板,填充报表参数。然后转化为为一个二进制数据。
2。将二进制数据然后发送到服务器页面,等待响应。
3。服务器页面接受二进制数据,调用报表引擎解析出报表模板及其参数。
4。报表引擎连接数据库,执行报表,生成报表文档,然后将报表文档转化为二进制数据。
5。服务器页面将这个二进制数据作为HTTP响应返回给客户端。
6。客户端程序接受这个二进制数据,调用报表引擎解析并生成报表文档,然后就可以进行查看或打印了。

一开始我没有使用标准的WebService方式来实现,本人比较老土,以前的开发中从未使用过WebService,但我有点HTTP协议的基础知识和一定的XML的知识,看了几篇WebService的介绍文章,也就自认为理解的WebService的原理和基本过程了,个人认为所有的Web服务器都是WebService,只是以前传递的都是比较粗放的HTML文档,接受者是浏览器。现在改成传递严谨的XML文档而已,接受者则是各种比浏览器聪明的应用程序。

开始自定义WebService了,客户端调用报表引擎,将填充了参数的报表模板文档转化为字节数组,考虑到以后可能要附加其他的数据,给数据加了个XML外壳,然后使用了System.Net.HttpWebRequest连接到指定的服务器页面地址,使用POST方法发送到服务器页面,然后等待响应。
在Web服务器上新建了一个ASPX页面,删除了ASPX文件的内容,只保留第一行。然后在该页面的Page_Load函数中使用this.Request.InputStream来加载XML文档,解析其中的二进制数据,进而使用报表引擎解析中报表模板,然后连接数据库执行报表模板生成报表文档对象,将报表文档对象转化为二进制数据,然后使用this.Resonse.BinaryWrite 写入到输出流中。
客户端程序接受到所有的HTTP响应后使用报表引擎将所得的字节数组解析为报表文档,然后就可以查看和预览了。

程序很快写出来了,在我的机器上测试通过,可是在某些机器上运行失败,可能是IIS的配置导致XML文档的编码格式问题,心中甚寒,感觉自定义的就是有些不可靠。决定还是使用标准的WebService吧。

找了一些简单的在VS.NET中使用WebService的例子,尝试了一下也就做出来了,发现VS.NET中使用WebService还是很容易的。在Web工程中插入一个asmx文件,写入以下代码

[System.Web.Services.WebMethod]
public byte[] ExecuteReport( byte[] bs )
{
   
using( System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection() )
   {
      conn.ConnectionString 
= "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + System.IO.Path.Combine( this.Server.MapPath(".") , "demomdb.mdb");
      conn.Open();
      XDesigner.Report.ReportBuilder builder 
= new XDesigner.Report.ReportBuilder();
      builder.DBConnection 
= conn ;
      
if( builder.Load( bs ) )
      {
         builder.RefreshContent();
         
return builder.SaveReportBinary();
      }
   }
   
return null;
}

类型XDesigner.Report.ReportBuilder是报表引擎的一个接口,用于加载和执行报表模板,生成报表文档。

如此服务器程序大功告成。

客户端程序那就是加入Web引用的方式生成一个映射到ExecuteReport的接口,然后应用程序就可以使用这个映射接口来调用WebService了。

但客户端程序要加入Web引用还是比较麻烦的,而且报表设计器也应当可以独立运行,于是想办法把这个Web引用给去掉。

使用Windows资源管理器,我找到了VS.NET的IDE添加Web引用时自动生成的代码文件,改代码如下

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute(
"code")]
[System.Web.Services.WebServiceBindingAttribute(Name
="XReportServiceSoap", Namespace="http://tempuri.org/")]
public class InnerXReportService : System.Web.Services.Protocols.SoapHttpClientProtocol
{
    
/// <remarks/>
    public InnerXReportService() 
    {
        
this.Url = "http://localhost/asp.net/report.asmx";
    }
        
/// <remarks/>
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/ExecuteReport", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    [
return: System.Xml.Serialization.XmlElementAttribute(DataType="base64Binary")]
    
public System.Byte[] ExecuteReport([System.Xml.Serialization.XmlElementAttribute(DataType="base64Binary")] System.Byte[] bs) 
    {
        
object[] results = this.Invoke("ExecuteReport"new object[] {
                                                                         bs});
        
return ((System.Byte[])(results[0]));
    }
        
/// <remarks/>
    public System.IAsyncResult BeginExecuteReport(System.Byte[] bs, System.AsyncCallback callback, object asyncState) 
    {
        
return this.BeginInvoke("ExecuteReport"new object[] {
                                                                  bs}, callback, asyncState);
    }
        
    
/// <remarks/>
    public System.Byte[] EndExecuteReport(System.IAsyncResult asyncResult) 
    {
        
object[] results = this.EndInvoke(asyncResult);
        
return ((System.Byte[])(results[0]));
    }
}
//public class InnerXReportService : System.Web.Services.Protocols.SoapHttpClientProtocol

把这个类改造一下加入到客户端程序中,然后删除Web引用,此时客户端使用这个自定义的WebService客户端来调用WebService。结果不小心成功了。

好了,摸索完毕,现在是整理封装了。

封装目标是接口简单,使用方便,而且能让程序能方便的在直接连接数据库的模式和使用WebService的模式之间来回切换。于是打上 XDesigner.Report.ReportBuilder 的主意。ReportBuilder内部是采用直接连接数据库来执行报表,若将WebService方式强加到里面则有些强扭的瓜不甜,因为这两种模式处理过程相差太大了。于是在此上面派生一个新的类,重载了其中生成报表的过程。但接口不变,这样客户端程序就比较容易在这两种模式下来回切换了。

于是在ReportBuilder的基础上派生了一个WebServiceClient 对象,新增了ServerURL属性用于指明WebService的页面地址,重载了Refresh 函数,可以调用WebService来生成报表了.

在本报表工具提供的演示程序 XReportWinFormDemo 就演示了如何使用WebService方式来执行报表,而且报表设计器也支持WebService方式来预览报表。

报表设计器支持WebService方式来预览报表有特别的好处。XReport报表引擎支持数据源的扩展编程,而报表设计器内置的报表引擎是无法进行扩展的,因此当应用程序在WebService服务器程序中使用了数据源扩展编程,则报表设计器就能通过调用WebService的方式来利用扩展编程的能力。是的报表设计器能更紧密的配合应用系统来编制报表模板。关于这点日后还会发表相关的主题的文章。

XDesigner软件工作室( http://www.xdesigner.cn ) 2006-10-23

原文地址:https://www.cnblogs.com/xdesigner/p/537621.html