WebService 应用(3) - 提高WebService的性能
一、一个简单的Webservice示例
a. 在数据库中创建一个Database,名为WebServiceDemo,然后创建一个表student,插入相关测试数据。(我使用了4万多条数据,方便下面的比较测试)。
b. 在VS中创建一个Web Service 应用程序,并调加一个 WebMethod:GetDataSet,该方法返回用"Select * From Student"查询得到的DataSet。
public DataSet GetDataSet()
{
string sqlCmd = "Select * From Student";
SqlConnection sqlConn = new SqlConnection(szConn);
sqlConn.Open();
SqlDataAdapter dataAdapter = new SqlDataAdapter(sqlCmd, sqlConn);
DataSet sqlDS = new DataSet("Demo");
dataAdapter.Fill(sqlDS);
sqlConn.Close();
return sqlDS;
}
二、Webservice部署到本地
部署过程中,需要注意两个问题:
1. 需要为数据库添加‘机器名\ASPNET’登录名并给予权限, 不然在客户端调用时会出现异常: System.Data.SqlClient.SqlException: 用户 '......\ASPNET' 登录失败。
2. 需要为这个发布的service创建虚拟目录,不然会在这一行提示错误:
<authentication mode="Windows"/>
三、 创建客户端----Webservice使用者
新建一个winform程序,添加刚刚部署的webservice(References->Add Service Reference)。在form中添加一个DataGridView用来显示数据。下面会介绍几种方法,这里用一个 button的click事件对应处理一种情况,然后进行比较。
四、提高WebService的性能
然而这种直接返回dataset的方法,在数据量比较大的时候,传递处理慢,消耗网络资源较多。下面介绍几种常用的方法,将dataset序列化/压缩后传输。
1. Webservice方法返回DataSet对象用Binary序列化后的字节数组,然后在客户端进行反序列化。采用字节数组流的处理模式,易于处理。
[WebMethod(Description = "返回DataSet对象用Binary序列化后的字节数组")]
public byte[] GetDataSetBytes()
{
DataSet sqlDS = GetDataSet();
BinaryFormatter bFormatter = new BinaryFormatter();
MemoryStream mStream = new MemoryStream();
bFormatter.Serialize(mStream, sqlDS);
byte[] buffer = mStream.ToArray();
return buffer;
}
//Client
/// <summary>
/// 反序列化DataSet对象用Binary序列化后的字节数组
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
// DateTime dtBegin = DateTime.Now;
byte[] buffer = webService.GetDataSetBytes();
BinaryFormatter bFormatter = new BinaryFormatter();
DataSet dataSet = bFormatter.Deserialize(new MemoryStream(buffer)) as DataSet;
// this.label2.Text = string.Format("耗时:{0}", DateTime.Now - dtBegin + " " + buffer.Length.ToString());
// BindDataSet(dataSet);
}
2. Webservice方法返回DataSetSurrogate对象用Binary序列化后的字节数组。这里用到微软提供的开源组件DataSetSurrogate.dll进行序列化。
[WebMethod(Description = "返回DataSetSurrogate对象用Binary序列化后的字节数组")]
public byte[] GetDataSetSurrogateBytes()
{
DataSet sqlDS = GetDataSet();
DataSetSurrogate dss = new DataSetSurrogate(sqlDS);
BinaryFormatter bFormatter = new BinaryFormatter();
MemoryStream mStream = new MemoryStream();
bFormatter.Serialize(mStream, dss);
byte[] buffer = mStream.ToArray();
return buffer;
}
//Client
/// <summary>
/// 反序列化DataSetSurrogate对象用Binary序列化后的字节数组(Microsoft组件处理)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button3_Click(object sender, EventArgs e)
{
// DateTime dtBegin = DateTime.Now;
byte[] buffer = webService.GetDataSetSurrogateBytes();
BinaryFormatter bFormatter = new BinaryFormatter();
DataSetSurrogate dss = bFormatter.Deserialize(new MemoryStream(buffer)) as DataSetSurrogate;
DataSet dataSet = dss.ConvertToDataSet();
// this.label3.Text = string.Format("耗时:{0}", DateTime.Now - dtBegin + " " + buffer.Length.ToString());
// BindDataSet(dataSet);
}
3. Webservice方法返回DataSetSurrogate对象用Binary序列化并ZIP压缩后的字节数组。
[WebMethod(Description = "返回DataSetSurrogate对象用Binary序列化并ZIP压缩后的字节数组")]
public byte[] GetDataSetSurrogateZipBytes()
{
DataSet sqlDS = GetDataSet();
DataSetSurrogate dss = new DataSetSurrogate(sqlDS);
BinaryFormatter bFormatter = new BinaryFormatter();
MemoryStream mStream = new MemoryStream();
bFormatter.Serialize(mStream, dss);
byte[] buffer = mStream.ToArray();
byte[] zipBuffer = Compress(buffer);
return zipBuffer;
}
/// <summary>
/// zip 压缩
/// </summary>
/// <param name="data">初始字节数组</param>
/// <returns>压缩后的字节数组</returns>
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
/// <summary>
/// 返回DataSetSurrogate对象用Binary序列化并ZIP压缩后的字节数组(Microsoft组件处理)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button4_Click(object sender, EventArgs e)
{
DateTime dtBegin = DateTime.Now;
byte[] zipBuffer = webService.GetDataSetSurrogateZipBytes();
byte[] buffer = Decompress(zipBuffer);
BinaryFormatter bFormatter = new BinaryFormatter();
DataSetSurrogate dss = bFormatter.Deserialize(new MemoryStream(buffer)) as DataSetSurrogate;
DataSet dataSet = dss.ConvertToDataSet();
this.label4.Text = string.Format("耗时:{0}", DateTime.Now - dtBegin + " " + zipBuffer.Length.ToString());
BindDataSet(dataSet);
}
/// <summary>
/// Decompresses the specified data.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
private 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 = EtractBytesFormStream(zipStream, data.Length);
return dc_data;
}
catch
{
return null;
}
}
private byte[] EtractBytesFormStream(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;
}
}
下图是四种方法的测试结果:(Sql Server2005, 数据line>40000,4个字段)