一、什麼是WebService:
簡單通俗來說,就是企業之間、網站之間通過Internet來通路并使用線上服務,一些資料,由于安全性問題,不能提供資料庫給其他機關使用,這時候可以使 用WebService服務提供。
二、建立WebService
建立WebService之後,我們就可以在檔案裡寫傳回資料的方法了。
三、傳回資料的四種形式
筆者水準有限,隻列出這四種資料的傳回形式:
(1)直接傳回DataSet對象
(2)傳回DataSet對象用Binary序列化後的位元組數組
(3)傳回DataSetSurrogate對象用Binary序列化後的 位元組數組
(4)傳回DataSetSurrogate對象用Binary序列化并Zip 壓縮後的位元組數組
理論上來說,網絡傳輸位元組與傳輸時間,應該是遞減的,其中,(3)(4)種方法需要引用微軟提供的開源元件 下載下傳位址:http://support.microsoft.com/kb/829740/zh-cn
下面展示這四種傳回資料的代碼,其中(1)是其三種方法的根本,都要得到一個DataSet作為根本,然後來做各種轉換壓縮的操作:
view source
print?
1 | [WebMethod(Description = "直接傳回DataSet對象" )] |
2 | public DataSet GetDataSet() |
4 | string connStr = System.Configuration.ConfigurationManager.ConnectionStrings[ "conn" ].ToString(); |
5 | SqlConnection conn = new SqlConnection(connStr); |
6 | string sql = "select * from china_city" ; |
8 | SqlDataAdapter sda = new SqlDataAdapter(sql, conn); |
9 | DataSet ds = new DataSet( "China" ); |
15 | [WebMethod(Description = "直接傳回DataSet對象,并用Binary序列化後的位元組數組" )] |
16 | public byte [] GetDataSetBytes() |
18 | DataSet ds = GetDataSet(); |
19 | BinaryFormatter ser = new BinaryFormatter(); //序列化對象 |
20 | MemoryStream ms = new MemoryStream(); //記憶體流 |
21 | ser.Serialize(ms, ds); |
22 | byte [] buffer = ms.ToArray(); //位元組流 |
26 | [WebMethod(Description = "直接傳回DataSetSurrogate對象,并用Binary序列化後的位元組數組" )] |
27 | public byte [] GetDataSetSurrogateBytes() |
29 | DataSet ds = GetDataSet(); |
30 | DataSetSurrogate dss = new DataSetSurrogate(ds); |
31 | BinaryFormatter ser = new BinaryFormatter(); //序列化對象 |
32 | MemoryStream ms = new MemoryStream(); //記憶體流 |
33 | ser.Serialize(ms, dss); |
34 | byte [] buffer = ms.ToArray(); //位元組流 |
39 | [WebMethod(Description = "直接傳回DataSetSurrogate對象,并用Binary序列化後并且ZIP壓縮的位元組數組" )] |
40 | public byte [] GetDataSetSurrogateZipBytes() |
42 | DataSet ds = GetDataSet(); |
43 | DataSetSurrogate dss = new DataSetSurrogate(ds); |
44 | BinaryFormatter ser = new BinaryFormatter(); //序列化對象 |
45 | MemoryStream ms = new MemoryStream(); //記憶體流 |
46 | ser.Serialize(ms, dss); |
47 | byte [] buffer = ms.ToArray(); //位元組流 |
48 | byte [] bufferZip = ComPress(buffer); |
52 | public byte [] ComPress( byte [] data) |
56 | MemoryStream ms = new MemoryStream(); |
57 | Stream zipStream = null ; |
58 | zipStream = new GZipStream(ms, CompressionMode.Compress, true ); |
59 | zipStream.Write(data, 0, data.Length); |
62 | byte [] compressed_data = new byte [ms.Length]; |
63 | ms.Read(compressed_data, 0, int .Parse(ms.Length.ToString())); |
64 | return compressed_data; |
我們可以在浏覽器中檢視下WebService的效果,如圖,在這個頁面中,有提供四個方法,這四個方法就是上述我們寫的四個傳回資料的方法了,點選方法即可傳回相應的資料,這樣,我們資料提供方的代碼就可以寫好了,接下來,我們寫調用資料的方法!
四、調用資料
用戶端WebService程式
1 | private void button1_Click( object sender, EventArgs e) |
3 | com.dzbsoft.www.Service1 ds = new com.dzbsoft.www.Service1(); //new出WebService對象 |
4 | DateTime dtBegin = DateTime.Now; |
5 | DataSet dataSet = ds.GetNorthwindDataSet(); |
6 | this .label1.Text = string .Format( "耗時:{0}" , DateTime.Now - dtBegin); |
9 | private void button2_Click( object sender, EventArgs e) |
11 | com.dzbsoft.www.Service1 ds = new com.dzbsoft.www.Service1(); |
12 | DateTime dtBegin = DateTime.Now; |
13 | byte [] buffer = ds.GetDataSetBytes(); |
14 | BinaryFormatter ser = new BinaryFormatter(); |
15 | DataSet dataSet = ser.Deserialize( new MemoryStream(buffer)) as DataSet; |
16 | this .label2.Text = string .Format( "耗時:{0}" , DateTime.Now - dtBegin) + " " + buffer.Length; |
19 | private void button3_Click( object sender, EventArgs e) |
21 | com.dzbsoft.www.Service1 ds = new com.dzbsoft.www.Service1(); |
22 | DateTime dtBegin = DateTime.Now; |
23 | byte [] buffer = ds.GetDataSetSurrogateBytes(); |
24 | BinaryFormatter ser = new BinaryFormatter(); |
25 | DataSetSurrogate dss = ser.Deserialize( new MemoryStream(buffer)) as DataSetSurrogate; |
26 | DataSet dataSet = dss.ConvertToDataSet(); |
27 | this .label3.Text = string .Format( "耗時:{0}" , DateTime.Now - dtBegin) + " " + buffer.Length; |
30 | private void button4_Click( object sender, EventArgs e) |
32 | com.dzbsoft.www.Service1 ds = new com.dzbsoft.www.Service1(); |
33 | DateTime dtBegin = DateTime.Now; |
34 | byte [] zipBuffer = ds.GetDataSetSurrogateZipBytes(); |
35 | byte [] buffer = UnZipClass.Decompress(zipBuffer); |
36 | BinaryFormatter ser = new BinaryFormatter(); |
37 | DataSetSurrogate dss = ser.Deserialize( new MemoryStream(buffer)) as DataSetSurrogate; |
38 | DataSet dataSet = dss.ConvertToDataSet(); |
39 | this .label4.Text = string .Format( "耗時:{0}" , DateTime.Now - dtBegin) + " " + zipBuffer.Length; |
42 | private void binddata(DataSet dataSet) |
44 | this .dataGridView1.DataSource = dataSet.Tables[0]; |
45 | this .label5.Text = "共計:" + dataSet.Tables[0].Rows.Count + "條記錄" ; |
在資料傳回的方法中,我們使用了資料的壓縮,是以,在調用方這邊,需要進行解壓,代碼:
2 | public static class UnZipClass |
4 | public static byte [] Decompress( byte [] data) |
8 | MemoryStream ms = new MemoryStream(data); |
9 | Stream zipStream = null ; |
10 | zipStream = new GZipStream(ms, CompressionMode.Decompress); |
11 | byte [] dc_data = null ; |
12 | dc_data = ExtractBytesFromStream(zipStream, data.Length); |
20 | public static byte [] ExtractBytesFromStream(Stream zipStream, int dataBlock) |
23 | int totalBytesRead = 0; |
28 | Array.Resize( ref data, totalBytesRead + dataBlock + 1); |
29 | int bytesRead = zipStream.Read(data, totalBytesRead, dataBlock); |
34 | totalBytesRead += bytesRead; |
36 | Array.Resize( ref data, totalBytesRead); |
在上例中,調用四個方法的效果是一樣的,唯一不同的是,傳輸過程中,資料量大小和傳輸時間的差異。
============2=============
效率調用問題,是以,我回說說如何實作同步與異步調用 webservice,如果說得哪裡不對或者不好的地方,歡迎大家評論指導。
首先,什麼是同步,什麼是異步呢?打個比方來說,小明和小 華,互相打架,小明打了小華3下之後,小華才能打回小明,這叫同步,如果,小華勇敢點,在小明打了第一下開始做出反擊,也打回小明,這叫異步。 也就是說,隻能等待另外一個作業進行完才能進行下一個操作的叫同步,在另外一個作業進行的同時也進行其他操作,叫異步。
先建立一個webservice
3 | using System.Web.Services; |
4 | using System.Web.Services.Protocols; |
6 | [WebService(Namespace = "http://tempuri.org/" )] |
7 | [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] |
8 | //若要允許使用 ASP.NET AJAX 從腳本中調用此 Web 服務,請取消對下行的注釋。 |
9 | // [System.Web.Script.Services.ScriptService] |
10 | public class GetWebService : System.Web.Services.WebService |
14 | public string HelloWorld() |
17 | for ( long i = 0; i < 1000000000; i++) //循環10億次,目的是模仿大批量操作,這裡至少需要數秒的操作以便看出異步的效果 |
21 | return " Hello World" ; |
webservice建立好了,建立一個winform項目,引入webservice,我在引入webservice的時候,差點被坑爹了,原來。VS裡是提供
Add Service References 和 Add Web References
這兩種,其實就是年代遺留下來的問題。web引用是2.0版本的,而服務引用是3.5版本的,微軟為了保持向前相容的特性,也保留了這兩種方法,分别可以看這裡
添加web引用和添加服務引用有什麼差別?Add Service References 和 Add Web References 有啥差別?
項目右鍵 添加服務引用,如果你用的是VS2008,菜單可能是添加web引用。
如果是本地做學習測試之用的,浏覽器浏覽你建立的webservice,得到URL,如果是使用網絡上的webservice,這裡則輸入給予的URL位址,點選前往即可,
再看看左下角的進階按鈕嗎?點選進階吧!!
把生成異步操作(必須勾上,不然沒有異步方法)勾上,生成消息合同也需勾上,看到左下角的添加WEB引用了嗎?這就是基于.NET Framework2.0 的。點選确定即可完成引入webservice。
兩種不同版本的引入webservice也将造成代碼的不同,是以,為了說明這個問題,我們也把2.0的引入方法也說明一下。
2.0的引入方法更加簡潔,如果你在看浪曦的webservice視訊教程,肯定很熟悉這個界面。我個人也是比較喜歡這種方法的。
編寫代碼
1 | localhost.GetWebService webservice = new localhost.GetWebService(); //通過2.0的添加WEB引用需要這種方式new出webservice對象 |
3 | ServiceReference1.GetWebServiceSoapClient getWebService = new ServiceReference1.GetWebServiceSoapClient(); //通過添加服務引用需要這種方式new出webservice對象 |
6 | private void btnSyn_Click( object sender, EventArgs e) |
8 | string res = webservice.HelloWorld(); |
9 | this .textBox1.Text += "完成了" ; |
10 | this .textBox1.Text += res + System.Environment.NewLine; |
14 | private void btnAsyn_Click( object sender, EventArgs e) |
16 | //給HelloWorld方法注冊調用完成時執行的方法AsyncHelloWorldComplete |
17 | webservice.HelloWorldCompleted += new localhost.HelloWorldCompletedEventHandler(AsyncHelloWorldComplete); |
19 | webservice.HelloWorldAsync(); |
20 | this .textBox1.Text += "完成了" + System.Environment.NewLine; |
23 | //完成webservice操作時會執行的方法 |
24 | void AsyncHelloWorldComplete( object sender, localhost.HelloWorldCompletedEventArgs e) |
26 | string res = e.Result; |
27 | this .textBox1.Text += res + System.Environment.NewLine; |
代碼說明:
1、HelloWorld方法:同步直接調用webservice的方法,傳回結果時輸出“成功了”加上傳回的結果;
2、webservice.HelloWorldAsync() :開始異步調用webservice
3、HelloWorldCompleted是webservice為我們提供委托調用,意思是将操作完成時執行的操作給參數中的方法執行,本例給了AsyncHelloWorldComplete方法執行;
執行效果:運作本例程式,你會發現,同步調用方法中,“完成了”這句話會與執行結果“Hello World”一起輸出,在webservice還沒執行完成的時候,小華不會打小明;
而異步調用方法中,“完成了”這句話先是輸出到文本框中,等了數秒之後,再顯示“Hello World”。這就是同步與異步調用webservice的差別了
如果需要在WebForm中異步調用,需要在頁面屬性中設定可以異步:Async=”true”
============================3======================
在前兩講裡,我已經向大家示範了如何使用WebService、同步, 異步調用WebService,而在實際開發過程中,可能會有多個WebService接口供你選擇,而在程式執行過程中才決定使用哪一個 WebService的情況,而以前的情況往往是添加指定的web引用調用WebService,而這一講中,會講述動态調用WebService,也就是知道WebService的位址而不用使用添加引用的方法來調用WebService。
淺談WebService開發(一)
淺談WebService開發二(同步與異步調用)
首先貼出整個架構的示意圖(圖檔來自浪曦),其中ServiceHelper類包括下面所示的五個步驟。
首先實作ServiceHelper類,代碼如下:
1 | using System.Collections.Generic; |
6 | using System.Configuration; |
8 | using System.CodeDom.Compiler; |
10 | using System.Web.Services; |
11 | using System.Web.Services.Description; |
12 | using Microsoft.CSharp; |
14 | namespace InvokeWebService |
16 | public static class WebServiceHelper |
21 | /// <param name="url">WebService位址</param> |
22 | /// <param name="methodname">WebService方法名</param> |
23 | /// <param name="args">參數清單</param> |
24 | /// <returns>傳回object</returns> |
25 | public static object InvokeWebService(string url, string methodname, object[] args) |
27 | return InvokeWebService(url, null , methodname, args); |
32 | /// <param name="url">WebService位址</param> |
33 | /// <param name="classname">類名</param> |
34 | /// <param name="methodname">WebService方法名</param> |
35 | /// <param name="args">參數清單</param> |
36 | /// <returns>傳回object</returns> |
37 | public static object InvokeWebService(string url, string classname, string methodname, object[] args) |
39 | string @ namespace = "ServiceBase.WebService.DynamicWebLoad" ; |
40 | if (string.IsNullOrEmpty(classname)) |
42 | classname = WebServiceHelper.GetClassName(url); |
45 | WebClient wc = new WebClient(); |
46 | Stream stream = wc.OpenRead(url + "?WSDL" ); |
47 | ServiceDescription sd = ServiceDescription.Read(stream); |
48 | ServiceDescriptionImporter sdi = new ServiceDescriptionImporter(); |
49 | sdi.AddServiceDescription(sd, "" , "" ); |
50 | CodeNamespace cn = new CodeNamespace(@ namespace ); |
52 | CodeCompileUnit ccu = new CodeCompileUnit(); |
53 | ccu.Namespaces.Add(cn); |
55 | CSharpCodeProvider csc = new CSharpCodeProvider(); |
56 | ICodeCompiler icc = csc.CreateCompiler(); |
58 | CompilerParameters cplist = new CompilerParameters(); |
59 | cplist.GenerateExecutable = false ; |
60 | cplist.GenerateInMemory = true ; |
61 | cplist.ReferencedAssemblies.Add( "System.dll" ); |
62 | cplist.ReferencedAssemblies.Add( "System.XML.dll" ); |
63 | cplist.ReferencedAssemblies.Add( "System.Web.Services.dll" ); |
64 | cplist.ReferencedAssemblies.Add( "System.Data.dll" ); |
66 | CompilerResults cr = icc.CompileAssemblyFromDom(cplist, ccu); |
67 | if ( true == cr.Errors.HasErrors) |
69 | System.Text.StringBuilder sb = new StringBuilder(); |
70 | foreach (CompilerError ce in cr.Errors) |
72 | sb.Append(ce.ToString() + System.Environment.NewLine); |
74 | throw new Exception(sb.ToString()); |
77 | System.Reflection.Assembly assembly = cr.CompiledAssembly; |
78 | Type t = assembly.GetType(@ namespace + "." + classname, true , true ); |
79 | object obj = Activator.CreateInstance(t); |
80 | System.Reflection.MethodInfo mi = t.GetMethod(methodname); |
81 | return mi.Invoke(obj, args); |
85 | /// 得到URL中的WebService名稱 |
87 | /// <param name="url">URL位址</param> |
88 | /// <returns>如http://wwww.baidu.com/service.asmx 則傳回service</returns> |
89 | private static string GetClassName(string url) |
91 | string[] parts = url.Split( '/' ); |
92 | string[] pps = parts[parts.Length - 1 ].Split( '.' ); |
然後,我們可以建立1個WebService,看看是如何動态調用的:
1 | private void button1_Click( object sender, EventArgs e) |
3 | string url = "http://localhost:2697/Service1.asmx" ; //用于做測試的WebService |
4 | object b = InvokeWebService.WebServiceHelper.InvokeWebService(url, "HelloWorld" , null ); |
5 | MessageBox.Show(b.ToString()); |