通過WebService來使用報表
WebService由于其中的數(shù)據(jù)經(jīng)過了XML文檔打包,因此其實際傳送的數(shù)據(jù)量大于實際的信息量,而且使用了HTTP這種比較低效的連接方式,因此其運行速度比較慢。但它是國際標準,是跨平臺交換數(shù)據(jù)的好方法,因此得到大大小小的軟件廠商的支持,正在普及應(yīng)用。
相信有很多應(yīng)用系統(tǒng),基本結(jié)構(gòu)是C/S系統(tǒng),但此時應(yīng)用系統(tǒng)不再直接連接數(shù)據(jù)庫了,而是使用WebService從一個Web服務(wù)器上下載DataSet來訪問數(shù)據(jù)。這樣有諸多好處,
1。安全,客戶端不用存放數(shù)據(jù)庫連接字符串這樣的敏感信息了。
2。其次是部署方便,不用在客戶端安裝各種數(shù)據(jù)庫客戶端API,不要進行各種配置了。
3。優(yōu)化數(shù)據(jù)庫的訪問,此時只有服務(wù)器程序直接連接數(shù)據(jù)庫,客戶端不連接數(shù)據(jù)庫,因此服務(wù)器程序可以放心的針對數(shù)據(jù)庫進行優(yōu)化。
4。可擴展性好,這種系統(tǒng)很多修改只限于服務(wù)器程序,而且比較容易轉(zhuǎn)換為純B/S系統(tǒng)。
有鑒于此,在下也趕趕時髦,想讓我的報表工具也支持這種WebService方式。
這種WebService的原理我設(shè)計的很簡單,其過程如下
1。報表設(shè)計器或運行在客戶端的報表引擎加載報表模板,填充報表參數(shù)。然后轉(zhuǎn)化為為一個二進制數(shù)據(jù)。
2。將二進制數(shù)據(jù)然后發(fā)送到服務(wù)器頁面,等待響應(yīng)。
3。服務(wù)器頁面接受二進制數(shù)據(jù),調(diào)用報表引擎解析出報表模板及其參數(shù)。
4。報表引擎連接數(shù)據(jù)庫,執(zhí)行報表,生成報表文檔,然后將報表文檔轉(zhuǎn)化為二進制數(shù)據(jù)。
5。服務(wù)器頁面將這個二進制數(shù)據(jù)作為HTTP響應(yīng)返回給客戶端。
6。客戶端程序接受這個二進制數(shù)據(jù),調(diào)用報表引擎解析并生成報表文檔,然后就可以進行查看或打印了。
一開始我沒有使用標準的WebService方式來實現(xiàn),本人比較老土,以前的開發(fā)中從未使用過WebService,但我有點HTTP協(xié)議的基礎(chǔ)知識和一定的XML的知識,看了幾篇WebService的介紹文章,也就自認為理解的WebService的原理和基本過程了,個人認為所有的Web服務(wù)器都是WebService,只是以前傳遞的都是比較粗放的HTML文檔,接受者是瀏覽器。現(xiàn)在改成傳遞嚴謹?shù)腦ML文檔而已,接受者則是各種比瀏覽器聰明的應(yīng)用程序。
開始自定義WebService了,客戶端調(diào)用報表引擎,將填充了參數(shù)的報表模板文檔轉(zhuǎn)化為字節(jié)數(shù)組,考慮到以后可能要附加其他的數(shù)據(jù),給數(shù)據(jù)加了個XML外殼,然后使用了System.Net.HttpWebRequest連接到指定的服務(wù)器頁面地址,使用POST方法發(fā)送到服務(wù)器頁面,然后等待響應(yīng)。
在Web服務(wù)器上新建了一個ASPX頁面,刪除了ASPX文件的內(nèi)容,只保留第一行。然后在該頁面的Page_Load函數(shù)中使用this.Request.InputStream來加載XML文檔,解析其中的二進制數(shù)據(jù),進而使用報表引擎解析中報表模板,然后連接數(shù)據(jù)庫執(zhí)行報表模板生成報表文檔對象,將報表文檔對象轉(zhuǎn)化為二進制數(shù)據(jù),然后使用this.Resonse.BinaryWrite 寫入到輸出流中。
客戶端程序接受到所有的HTTP響應(yīng)后使用報表引擎將所得的字節(jié)數(shù)組解析為報表文檔,然后就可以查看和預(yù)覽了。
程序很快寫出來了,在我的機器上測試通過,可是在某些機器上運行失敗,可能是IIS的配置導(dǎo)致XML文檔的編碼格式問題,心中甚寒,感覺自定義的就是有些不可靠。決定還是使用標準的WebService吧。
找了一些簡單的在VS.NET中使用WebService的例子,嘗試了一下也就做出來了,發(fā)現(xiàn)VS.NET中使用WebService還是很容易的。在Web工程中插入一個asmx文件,寫入以下代碼
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是報表引擎的一個接口,用于加載和執(zhí)行報表模板,生成報表文檔。
如此服務(wù)器程序大功告成。
客戶端程序那就是加入Web引用的方式生成一個映射到ExecuteReport的接口,然后應(yīng)用程序就可以使用這個映射接口來調(diào)用WebService了。
但客戶端程序要加入Web引用還是比較麻煩的,而且報表設(shè)計器也應(yīng)當可以獨立運行,于是想辦法把這個Web引用給去掉。
使用Windows資源管理器,我找到了VS.NET的IDE添加Web引用時自動生成的代碼文件,改代碼如下
[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客戶端來調(diào)用WebService。結(jié)果不小心成功了。
好了,摸索完畢,現(xiàn)在是整理封裝了。
封裝目標是接口簡單,使用方便,而且能讓程序能方便的在直接連接數(shù)據(jù)庫的模式和使用WebService的模式之間來回切換。于是打上 XDesigner.Report.ReportBuilder 的主意。ReportBuilder內(nèi)部是采用直接連接數(shù)據(jù)庫來執(zhí)行報表,若將WebService方式強加到里面則有些強扭的瓜不甜,因為這兩種模式處理過程相差太大了。于是在此上面派生一個新的類,重載了其中生成報表的過程。但接口不變,這樣客戶端程序就比較容易在這兩種模式下來回切換了。
于是在ReportBuilder的基礎(chǔ)上派生了一個WebServiceClient 對象,新增了ServerURL屬性用于指明WebService的頁面地址,重載了Refresh 函數(shù),可以調(diào)用WebService來生成報表了.
在本報表工具提供的演示程序 XReportWinFormDemo 就演示了如何使用WebService方式來執(zhí)行報表,而且報表設(shè)計器也支持WebService方式來預(yù)覽報表。
報表設(shè)計器支持WebService方式來預(yù)覽報表有特別的好處。XReport報表引擎支持數(shù)據(jù)源的擴展編程,而報表設(shè)計器內(nèi)置的報表引擎是無法進行擴展的,因此當應(yīng)用程序在WebService服務(wù)器程序中使用了數(shù)據(jù)源擴展編程,則報表設(shè)計器就能通過調(diào)用WebService的方式來利用擴展編程的能力。是的報表設(shè)計器能更緊密的配合應(yīng)用系統(tǒng)來編制報表模板。關(guān)于這點日后還會發(fā)表相關(guān)的主題的文章。
XDesigner軟件工作室( http://www.xdesigner.cn ) 2006-10-23
posted on 2006-10-23 20:12 袁永福 電子病歷,醫(yī)療信息化 閱讀(2262) 評論(0) 收藏 舉報
浙公網(wǎng)安備 33010602011771號