提高WebService大数据量网络传输处理的性能
直接返回DataSet对象
特点:通常组件化的处理机制,不加任何修饰及处理;
优点:代码精减、易于处理,小数据量处理较快;
缺点:大数据量的传递处理慢,消耗网络资源;
建议:当应用系统在内网、专网(局域网)的应用时,或外网(广域网)且数据量在KB级时的应用时,采用此种模式。
public DataSet GetDataSet() { string sql = "select * from XT_TEXT"; SqlConnection conn = new SqlConnection("Server=xxxxx;DataBase=s168593;user id=s168593;password=h0y+FeC*;"); conn.Open(); SqlDataAdapter dataAd = new SqlDataAdapter(sql, conn); DataSet DS = new DataSet("XT_TEXT"); dataAd.Fill(DS); conn.Close(); return DS; }
Client:
private void BindDataSet(DataSet DS) { this.dataGridView1.DataSource = DS.Tables[0]; } private void button1_Click(object sender, EventArgs e) { com.dzbsoft.www.DataSetService ds = new Test.com.dzbsoft.www.DataSetService(); DateTime dtBegin = DateTime.Now; DataSet DS = ds.GetDataSet(); this.label1.Text = string.Format("耗时:{0}", DateTime.Now - dtBegin); BindDataSet(DS); }
返回DataSet对象用Binary序列化后的字节数组
特点:字节数组流的处理模式;
优点:易于处理,可以中文内容起到加密作用;
缺点:大数据量的传递处理慢,较消耗网络资源;
建议:当系统需要进行较大数据交换时采用。
public byte[] GetDataSetBytes() { DataSet DS = GetDataSet(); BinaryFormatter ser = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); ser.Serialize(ms, DS); byte[] buffer = ms.ToArray(); return buffer; }
Client:
private void button2_Click(object sender, EventArgs e) { com.dzbsoft.www.DataSetService ds = new Test.com.dzbsoft.www.DataSetService(); DateTime dtBegin = DateTime.Now; byte[] buffer = ds.GetDataSetBytes(); DataSet DS = ds.GetDataSet(); BinaryFormatter ser = new BinaryFormatter(); DataSet dataset = ser.Deserialize(new MemoryStream(buffer)) as DataSet; this.label2.Text = string.Format("耗时:{0}", DateTime.Now - dtBegin + " " + buffer.Length.ToString()); BindDataSet(DS); }
返回DataSetSurrogate对象用Binary序列化后的字节数组
特点:微软提供的开源组件;下载地址:http://support.microsoft.com/kb/829740/zh-cn
优点:易于处理,可以中文内容起到加密作用;
缺点:大数据量的传递处理慢,较消耗网络资源;
建议:当系统需要进行较大数据交换时采用。
public byte[] GetDataSetSurrogateBytes() { DataSet DS = GetDataSet(); DataSetSurrogate dss = new DataSetSurrogate(DS); BinaryFormatter ser = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); ser.Serialize(ms, dss); byte[] buffer = ms.ToArray(); return buffer; }
Client:
private void button3_Click(object sender, EventArgs e) { com.dzbsoft.www.DataSetService ds = new Test.com.dzbsoft.www.DataSetService(); DateTime dtBegin = DateTime.Now; byte[] buffer = ds.GetDataSetSurrogateBytes(); BinaryFormatter ser = new BinaryFormatter(); DataSet DS = ds.GetDataSet(); DataSetSurrogate dss = ser.Deserialize(new MemoryStream(buffer)) as DataSetSurrogate; DataSet dataset = dss.ConvertToDataSet(); this.label3.Text = string.Format("耗时:{0}", DateTime.Now - dtBegin + " " + buffer.Length.ToString()); BindDataSet(DS); }
返回DataSetSurrogate对象用Binary序列化并Zip压缩后的字节数组
特点:对字节流数组进行压缩后传递;
优点:当数据量大时,性能提高效果明显,压缩比例大;
缺点:相比第三方组件,压缩比例还有待提高;
建议:当系统需要进行大数据量网络数据传递时,建议采用此种可靠、高效、免费的方法。
public byte[] GetDataSetSurrogateZipBytes() { DataSet DS = GetDataSet(); DataSetSurrogate dss = new DataSetSurrogate(DS); BinaryFormatter ser = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); ser.Serialize(ms, dss); byte[] buffer = ms.ToArray(); byte[] Zipbuffer = Compress(buffer); return Zipbuffer; } public byte[] Compress(byte[] data) { MemoryStream ms = new MemoryStream(); Stream zipStream = null; zipStream = new GZipStream(ms, CompressionMode.Compress, true); zipStream.Write(data, 0, data.Length); zipStream.Close(); ms.Position = 0; byte[] compressed_data = new byte[ms.Length]; ms.Read(compressed_data, 0, int.Parse(ms.Length.ToString())); return compressed_data; }
Client:
private void button4_Click(object sender, EventArgs e) { com.dzbsoft.www.DataSetService ds = new Test.com.dzbsoft.www.DataSetService(); DateTime dtBegin = DateTime.Now; byte[] zipBuffer = ds.GetDataSetSurrogateZipBytes(); byte[] buffer = UnZipClass.Decompress(zipBuffer); BinaryFormatter ser = new BinaryFormatter(); DataSet DS = ds.GetDataSet(); DataSetSurrogate dss = ser.Deserialize(new MemoryStream(buffer)) as DataSetSurrogate; DataSet dataset = dss.ConvertToDataSet(); this.label4.Text = string.Format("耗时:{0}", DateTime.Now - dtBegin + " " + zipBuffer.Length.ToString()); BindDataSet(DS); }
public static class UnZipClass { /// <summary> /// Decompresses the specified data. /// </summary> /// <param name="data">The data.</param> /// <returns></returns> public static byte[] Decompress(byte[] data) { try { MemoryStream ms = new MemoryStream(data); Stream zipStream = null; zipStream = new GZipStream(ms, CompressionMode.Decompress); byte[] dc_data = null; dc_data = ExtractBytesFormStream(zipStream, data.Length); return dc_data; } catch { return null; } } public static byte[] ExtractBytesFormStream(Stream zipStream, int dataBlock) { try { byte[] data = null; int totalBytesRead = 0; while (true) { Array.Resize(ref data, totalBytesRead + dataBlock + 1); int bytesRead = zipStream.Read(data, totalBytesRead, dataBlock); if (bytesRead == 0) { break; } totalBytesRead += bytesRead; } Array.Resize(ref data, totalBytesRead); return data; } catch { return null; } } }
测试用例:SqlServer2000数据库,数据量大小40000行,字段数10个,结果如下:
使用方法 | 总用时(秒) | 数据大小(byte) |
大小百分比(%) |
直接返回DataSet | 12.625 | 19629414 | 100% |
返回二进制序列化后DataSet | 9.712 | 12049645 | 61.38% |
返回转化DataSetSurrogate的DataSet并且二进制序列化后 | 7.943 | 5138990 | 26.18% |
返回转化DataSetSurrogate的DataSet并且二进制序列化后使用zip压缩 | 7.619 | 978033 | 4.98% |
WebService中的异步调用
调用WebService方法有两种方式,同步调用和异步调用。同步调用是程序继续执行前等候调用的完成,而异步调用在后台继续时,程序也继续执行,不必等待方法处理完成而直接返回。
同步模式:
优点:稳定、安全、可靠;
缺点:效率低下。
异步模式:
优点:高效、充分利用资源;
缺点:事务处理较为复杂,不易于控制。
下面的Demo是通过ado.net对数据库表导入导出的操作。
Host:
public class AsynWebService : System.Web.Services.WebService { [WebMethod(Description = "复制表数据表数据库表")] public string CopyDataTable(string Pub_CnnD, string DestTableName, byte[] ZipByte) { byte[] buffer = UnZipClass.Decompress(ZipByte); BinaryFormatter ser = new BinaryFormatter(); DataSetSurrogate dss; dss = (DataSetSurrogate)ser.Deserialize(new MemoryStream(buffer)); DataSet DS = dss.ConvertToDataSet(); int RowNum; RowNum = DS.Tables[0].Rows.Count; bool Result = DataTableFillToDb(Pub_CnnD, DestTableName, DS.Tables[0]); DS.Clear(); DS.Reset(); GC.Collect(); if (Result == true) { return "复制表:" + DestTableName + " 的数据到远程数据成功!记录数为:" + RowNum.ToString(); } else { return "复制表:" + DestTableName + " 的数据到远程数据失败!"; } } /// <summary> /// 表存在的DataTable填充 /// </summary> /// <param name="Pub_CnnD">数据库联结串</param> /// <param name="DestTableName">目标表名</param> /// <param name="SouseDataTable">源DataTable</param> /// <returns></returns> private bool DataTableFillToDb(string Pub_CnnD, string DestTableName, DataTable SouseDataTable) { try { if (SouseDataTable.Rows.Count > 0) { SqlConnection SqlCnn = new SqlConnection(Pub_CnnD); SqlCnn.Open(); DataTable NewTable = OrdinalTable(Pub_CnnD, SouseDataTable, SouseDataTable.TableName); SqlBulkCopy Bcp = new SqlBulkCopy(SqlCnn); Bcp.DestinationTableName = DestTableName; Bcp.WriteToServer(NewTable); Bcp.Close(); SqlCnn.Close(); } SouseDataTable.Reset(); SouseDataTable.Dispose(); return true; } catch { return false; } } /// <summary> /// 调整表字段顺序 /// </summary> /// <param name="SqlConnstring">数据库联结串</param> /// <param name="DTS">源DataTable</param> /// <param name="TableName">表名</param> /// <returns></returns> private DataTable OrdinalTable(string SqlConnstring, DataTable DTS, string TableName) { try { System.Data.SqlClient.SqlConnection SqldbCnn = new System.Data.SqlClient.SqlConnection(SqlConnstring); DataTable DTD = new DataTable(); System.Data.SqlClient.SqlDataAdapter SqlDbDA = new System.Data.SqlClient.SqlDataAdapter("select top 1 * from " + TableName, SqldbCnn); SqldbCnn.Open(); SqlDbDA.Fill(DTD); SqlDbDA.Dispose(); SqldbCnn.Close(); DTD.Clear(); int I = 0; int J = 0; string CNS = ""; string CND = ""; int MaxCol = 0; for (I = 0; I <= DTS.Columns.Count - 1; I++) { CNS = DTS.Columns[I].ColumnName; for (J = 0; J <= DTD.Columns.Count - 1; J++) { CND = DTD.Columns[J].ColumnName; if (CND.ToUpper() == CNS.ToUpper()) { if (MaxCol < J) { MaxCol = J; break; } } } } for (J = 0; J <= MaxCol; J++) { if (DTS.Columns.IndexOf(DTD.Columns[J].ColumnName) < 0) { DTS.Columns.Add(DTD.Columns[J].ColumnName.ToUpper(), DTD.Columns[J].DataType); } } for (I = 0; I <= DTD.Columns.Count - 1; I++) { CND = DTD.Columns[I].ColumnName; for (J = 0; J <= DTS.Columns.Count - 1; J++) { CNS = DTS.Columns[J].ColumnName; if (CNS.ToUpper() == CND.ToUpper()) { DTS.Columns[J].SetOrdinal(I); break; } } } return DTS; } catch { return DTS; } }
Client:
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private string DestConnstring = "Server=60.28.25.58;DataBase=s168593;user id=s168593;password=h0y+FeC*;"; private string SourceConnstring = "Server=.;DataBase=XZBWWW;user id=sa;password=sasa;"; private void Form1_Load(object sender, EventArgs e) { SqlConnection Sconn = new SqlConnection(SourceConnstring); Sconn.Open(); DataTable DT = new DataTable(); DT = Sconn.GetSchema("Tables"); this.listBox1.DisplayMember = "TABLE_NAME"; this.listBox1.DataSource = DT; Sconn.Close(); } private void listBox1_DoubleClick(object sender, EventArgs e) { localhost.AsynWebService Myservice = new Test.localhost.AsynWebService(); Myservice.CopyDataTableCompleted += new Test.localhost.CopyDataTableCompletedEventHandler(CopyDataTableEventHandler); DataRowView DRV=(DataRowView)this.listBox1.Items[this.listBox1.SelectedIndex]; DataSet DS=new DataSet(); DS=GetDataSet(SourceConnstring,"select * from " + DRV["TABLE_NAME"].ToString()); Myservice.CopyDataTableAsync(DestConnstring, DRV["TABLE_NAME"].ToString(), GetDataSetSurrogateZipBYtes(DS)); this.textBox1.Text = this.textBox1.Text + "运行本模块完成!"; } private void CopyDataTableEventHandler(object sender, localhost.CopyDataTableCompletedEventArgs e) { this.textBox1.Text = this.textBox1.Text + e.Result + System.Environment.NewLine; } public DataSet GetDataSet(string StrCnn, string StrSql) { DataSet DS = new DataSet(); SqlDataAdapter DA = new SqlDataAdapter(StrSql, StrCnn); DA.Fill(DS); DA.Dispose(); return DS; } public byte[] GetDataSetSurrogateZipBYtes(DataSet DS) { DataSetSurrogate dss = new DataSetSurrogate(DS); BinaryFormatter ser = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); ser.Serialize(ms, dss); byte[] buffer = ms.ToArray(); byte[] Zipbuffer = Compress(buffer); return Zipbuffer; } private byte[] Compress(byte[] data) { MemoryStream ms = new MemoryStream(); Stream zipStream = null; zipStream = new GZipStream(ms, CompressionMode.Compress, true); zipStream.Write(data, 0, data.Length); zipStream.Close(); ms.Position = 0; byte[] Compressed_Data = new byte[ms.Length]; ms.Read(Compressed_Data, 0, int.Parse(ms.Length.ToString())); return Compressed_Data; } }
动态调用WebService
代码文档对象模型CodeDom的使用和编程使用C#编译器
public static class WebServiceHelper { /// <summary> /// 动态调用WebService /// </summary> /// <param name="url">WebService地址</param> /// <param name="methodname">方法名(模块名)</param> /// <param name="args">参数列表</param> /// <returns>object</returns> public static object InvokeWebService(string url, string methodname, object[] args) { return InvokeWebService(url, null, methodname, args); } /// <summary> /// 动态调用WebService /// </summary> /// <param name="url">WebService地址</param> /// <param name="classname">类名</param> /// <param name="methodname">方法名(模块名)</param> /// <param name="args">参数列表</param> /// <returns>object</returns> public static object InvokeWebService(string url, string classname, string methodname, object[] args) { string @namespace = "ServiceBase.WebService.DynamicWebLoad"; if (classname == null || classname == "") { classname = GetClassName(url); } //获取服务描述语言(WSDL) WebClient wc = new WebClient(); Stream stream = wc.OpenRead(url + "?WSDL"); ServiceDescription sd = ServiceDescription.Read(stream); ServiceDescriptionImporter sdi = new ServiceDescriptionImporter(); sdi.AddServiceDescription(sd, "", ""); CodeNamespace cn = new CodeNamespace(@namespace); //生成客户端代理类代码 CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(cn); sdi.Import(cn, ccu); CSharpCodeProvider csc = new CSharpCodeProvider(); ICodeCompiler icc = csc.CreateCompiler(); //设定编译器的参数 CompilerParameters cplist = new CompilerParameters(); cplist.GenerateExecutable = false; cplist.GenerateInMemory = true; cplist.ReferencedAssemblies.Add("System.dll"); cplist.ReferencedAssemblies.Add("System.XML.dll"); cplist.ReferencedAssemblies.Add("System.Web.Services.dll"); cplist.ReferencedAssemblies.Add("System.Data.dll"); //编译代理类 CompilerResults cr = icc.CompileAssemblyFromDom(cplist, ccu); if (true == cr.Errors.HasErrors) { System.Text.StringBuilder sb = new StringBuilder(); foreach (CompilerError ce in cr.Errors) { sb.Append(ce.ToString()); sb.Append(System.Environment.NewLine); } throw new Exception(sb.ToString()); } //生成代理实例,并调用方法 System.Reflection.Assembly assembly = cr.CompiledAssembly; Type t = assembly.GetType(@namespace + "." + classname, true, true); object obj = Activator.CreateInstance(t); System.Reflection.MethodInfo mi = t.GetMethod(methodname); return mi.Invoke(obj, args); } private static string GetClassName(string url) { string[] parts = url.Split(‘/‘); string[] pps = parts[parts.Length - 1].Split(‘.‘); return pps[0]; } }
Test WebService:
[WebMethod] public string HelloWorld(string Temp1,string Temp2) { return "参数1:" + Temp1 + " 参数2:" + Temp2; }
Client:
string url = "http://localhost/InvokeService/Service1.asmx"; string[] args = new string[2]; args[0] = this.textBox1.Text; args[1] = this.textBox2.Text; object result = InvokeWebService.WebServiceHelper.InvokeWebService(url, "HelloWorld", args); this.textBox3.Text = result.ToString();