认识并测量Typed DataSet
下载本文的源码(C#) - 17k
下载本文的源码(VB) - 15k
简介
强类型DataSet
强类型DataSet是从DataSet继承的定制对象,它可以通过其显露的属性(properties)来对封装数据进行强类型的访问。
强类型DataSet避免了字段的晚绑定。强类型DataSet提供了强类型的访问器,因为避免了到一个集合中查找列名或表名,访问时更快。
除了能够提高运行时的性能,强类型DataSet还提供了类型检查,并且在设计时可以通过自定义字段名对字段智能感知。
在实例化和封送的性能上,强类型的DataSet和普通的DataSet大致相当。主要的性能优势在于客户端可以直接访问方法和属性而毋须通过集合。
创建Typed DataSet
这里借用MSDN的一篇文章http://support.microsoft.com/default.aspx?scid=kb;en-us;320714
简单的说就是在项目上点右键—》添加新项-》数据集-》取名dsProducts,然后在从服务器资源管理器中将sqlserver展开找到Northwind的Alphabetical list of products视图拖入dsProducts的设计窗口-》保存。
性能测量
好啦,下面我们对通过两个简单的页面来对常规DataSet和Typed DataSet进行一下性能比较
完整源码(NormalDataSet.aspx)
<%@ Page language="c#" Codebehind="NormalDataSet.aspx.cs" AutoEventWireup="false" Inherits="STD.NormalDataSet" %>
<!DOCTYPE html public "-//w3c//dtd html 4.0 transitional//en" >
<HTML>
<HEAD>
<TITLE>NormalDataSet</TITLE>
<META name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
<META name="CODE_LANGUAGE" content="C#">
<META name=vs_defaultClientScript content="JavaScript">
<META name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<BODY ms_positioning="GridLayout">
<FORM id="Form1" method="post" runat="server">
<ASP:LABEL id=Label1 style="Z-INDEX: 101; LEFT: 250px; POSITION: absolute; TOP: 250px" runat="server" />
</FORM>
</BODY>
</HTML>
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="NormalDataSet.aspx.vb" Inherits="STDvb.NormalDataSet"%>
<!DOCTYPE html public "-//w3c//dtd html 4.0 transitional//en">
<HTML>
<HEAD>
<TITLE>NormalDataSet</TITLE>
<META name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
<META name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
<META name=vs_defaultClientScript content="JavaScript">
<META name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<BODY ms_positioning="GridLayout">
<FORM id="Form1" method="post" runat="server">
<ASP:LABEL id=Label1 style="Z-INDEX: 101; LEFT: 250px; POSITION: absolute; TOP: 250px" runat="server" />
</FORM>
</BODY>
</HTML>
|
C# |
VB |
隐藏代码 |
|
完整源码(NormalDataSet.aspx.cs/vb)
using System;
using System.Data;
using System.Data.SqlClient;
namespace STD
{
/// <SUMMARY>
/// WebForm2 的摘要说明。
/// </SUMMARY>
public class NormalDataSet : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Label Label1;
private void Page_Load(object sender, System.EventArgs e)
{
SqlConnection cn = new SqlConnection("server=localhost;uid=sa;pwd=;database=northwind");
SqlCommand cmd = new SqlCommand("select * from [Alphabetical list of products]", cn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet tds = new DataSet();
da.Fill(tds);
long begin = DateTime.Now.Ticks;
// 放大性能差异
for(int k =0;k<10000;k++)
{
for(int i = 0,j = tds.Tables[0].Rows.Count;i<J;i++)
{
label1.text = tds.Tables[0].Rows[i]["ProductName"].ToString();
}
}
long end = DateTime.Now.Ticks;
response.write("Total Time(ms): " + (end- begin));
}
#region web 窗体设计器生成的代码
override protected void oninit(eventargs e)
{
//
// CODEGEN: 该调用是 asp.net web 窗体设计器所必需的。
//
initializecomponent();
base.oninit(e);
}
/// <SUMMARY>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </SUMMARY>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
}
Imports System.Data.SqlClient
Public Class NormalDataSet
Inherits System.Web.UI.Page
#Region " Web 窗体设计器生成的代码 "
'该调用是 Web 窗体设计器所必需的。
<SYSTEM.DIAGNOSTICS.DEBUGGERSTEPTHROUGH()>
Private Sub InitializeComponent()
End Sub
Protected WithEvents Label1 As System.Web.UI.WebControls.Label
'注意: 以下占位符声明是 Web 窗体设计器所必需的。
'不要删除或移动它。
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN: 此方法调用是 Web 窗体设计器所必需的
'不要使用代码编辑器修改它。
InitializeComponent()
End Sub
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim cn As SqlConnection = New SqlConnection("server=localhost;uid=sa;pwd=;database=northwind")
Dim cmd As SqlCommand = New SqlCommand("select * from [Alphabetical list of products]", cn)
Dim da As SqlDataAdapter = New SqlDataAdapter(cmd)
Dim tds As DataSet = New DataSet
da.Fill(tds)
Dim lngBegin As Long = DateTime.Now.Ticks
' 放大性能差异
Dim k, i As Integer
For k = 0 To 10000
For i = 0 To tds.Tables(0).Rows.Count - 1
Label1.Text = tds.Tables(0).Rows(i)("ProductName").ToString()
Next
Next
Dim lngEnd As Long = DateTime.Now.Ticks
Response.Write("Total Time(ms): " & (lngEnd - lngBegin))
End Sub
End Class
|
C# |
VB |
隐藏代码 |
|
完整源码(TypedDataSet.aspx)
<%@ Page language="c#" Codebehind="TypedDataSet.aspx.cs" AutoEventWireup="false" Inherits="STD.TypedDataSet" %>
<!DOCTYPE html public "-//w3c//dtd html 4.0 transitional//en" >
<HTML>
<HEAD>
<TITLE>TypedDataSet</TITLE>
<META name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
<META name="CODE_LANGUAGE" content="C#">
<META name=vs_defaultClientScript content="JavaScript">
<META name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<BODY ms_positioning="GridLayout">
<FORM id="Form1" method="post" runat="server">
<ASP:LABEL id=Label1 style="Z-INDEX: 101; LEFT: 250px; POSITION: absolute; TOP: 250px" runat="server" />
</FORM>
</BODY>
</HTML>
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="TypedDataSet.aspx.vb" Inherits="STDvb.TypedDataSet"%>
<!DOCTYPE html public "-//w3c//dtd html 4.0 transitional//en">
<HTML>
<HEAD>
<TITLE>TypedDataSet</TITLE>
<META name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
<META name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
<META name=vs_defaultClientScript content="JavaScript">
<META name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
</HEAD>
<BODY ms_positioning="GridLayout">
<FORM id="Form1" method="post" runat="server">
<ASP:LABEL id=Label1 style="Z-INDEX: 101; LEFT: 250px; POSITION: absolute; TOP: 250px" runat="server" />
</FORM>
</BODY>
</HTML>
|
C# |
VB |
隐藏代码 |
|
完整源码(TypedDataSet.aspx.cs/TypedDataSet.aspx.vb)
using System;
using System.Data.SqlClient;
namespace STD
{
/// <SUMMARY>
/// WebForm1 的摘要说明。
/// </SUMMARY>
public class TypedDataSet : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Label Label1;
private void Page_Load(object sender, System.EventArgs e)
{
SqlConnection cn = new SqlConnection("server=.;uid=sa;pwd=;database=northwind");
SqlCommand cmd = new SqlCommand("select * from [Alphabetical list of products]", cn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
dsProducts tds = new dsProducts();
da.Fill(tds, tds.Tables[0].TableName);
long begin = DateTime.Now.Ticks;
// 放大性能差异
for(int k =0;k<10000;k++)
{
for(int i = 0,j = tds.Alphabetical_list_of_products.Rows.Count;i<J;i++)
{
label1.text = tds.Alphabetical_list_of_products[i].ProductName;
}
}
long end = DateTime.Now.Ticks;
response.write( end- begin);
}
#region web 窗体设计器生成的代码
override protected void oninit(eventargs e)
{
//
// CODEGEN: 该调用是 asp.net web 窗体设计器所必需的。
//
initializecomponent();
base.oninit(e);
}
/// <SUMMARY>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </SUMMARY>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
}
Imports System.Data.SqlClient
Public Class TypedDataSet
Inherits System.Web.UI.Page
#Region " Web 窗体设计器生成的代码 "
'该调用是 Web 窗体设计器所必需的。
<SYSTEM.DIAGNOSTICS.DEBUGGERSTEPTHROUGH()>
Private Sub InitializeComponent()
End Sub
Protected WithEvents Label1 As System.Web.UI.WebControls.Label
'注意: 以下占位符声明是 Web 窗体设计器所必需的。
'不要删除或移动它。
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN: 此方法调用是 Web 窗体设计器所必需的
'不要使用代码编辑器修改它。
InitializeComponent()
End Sub
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim cn As SqlConnection = New SqlConnection("server=localhost;uid=sa;pwd=;database=northwind")
Dim cmd As SqlCommand = New SqlCommand("select * from [Alphabetical list of products]", cn)
Dim da As SqlDataAdapter = New SqlDataAdapter(cmd)
Dim tds As dsProducts = New dsProducts
da.Fill(tds, tds.Tables(0).TableName)
Dim lngBegin As Long = DateTime.Now.Ticks
' 放大性能差异
Dim k, i As Integer
For k = 0 To 10000
For i = 0 To tds.Alphabetical_list_of_products.Rows.Count - 1
Label1.Text = tds.Alphabetical_list_of_products(i).ProductName
Next
Next
Dim lngEnd As Long = DateTime.Now.Ticks
Response.Write("Total Time(ms): " & (lngEnd - lngBegin))
End Sub
End Class
|
C# |
VB |
隐藏代码 |
|
测试结果(受操作系统,硬件环境,框架版本等影响,仅供参考) 注:以下结果是从页面运行结果取10次数值所得
页面 |
平均 |
最小 |
NormalDataSet.aspx(C#) |
4687500 |
4375000 |
NormalDataSet.aspx(VB) |
4687500 |
4687500 |
TypedDataSet.aspx(C#) |
2500000 |
2343750 |
TypedDataSet.aspx(VB) |
3437500 |
3281250 |
典型的ACT测试结果(只对C#进行了测试,vb的页面请自行测试比较
属性
测试类型: 动态
浏览器同时连接数: 1
准备时间(秒):
测试持续时间: 00:00:05:00
测试迭代次数: 586
生成的详细测试结果: 是
摘要
请求总数: 586
连接总数: 586
每秒平均请求数: 1.95
首字节平均响应时间(毫秒): 510.41
末字节平均响应时间(毫秒): 510.48
每次迭代末字节平均响应时间(毫秒): 510.48
测试中的唯一请求数: 1
唯一响应代码数: 1
错误计数
HTTP:
DNS:
套接字:
其他网络统计数据
平均带宽(字节/秒): 2,730.95
发送字节数(字节): 147,234
接收字节数(字节): 672,050
发送字节平均速率(字节/秒): 490.78
接收字节平均速率(字节/秒): 2,240.17
连接错误数:
发送错误数:
接收错误数:
超时错误数:
响应代码
Response Code: 200 - 请求已成功完成。
计数: 586
百分比(%): 100.00
属性
测试类型: 动态
浏览器同时连接数: 1
准备时间(秒):
测试持续时间: 00:00:05:00
测试迭代次数: 1,124
生成的详细测试结果: 是
摘要
请求总数: 1,124
连接总数: 1,124
每秒平均请求数: 3.75
首字节平均响应时间(毫秒): 265.19
末字节平均响应时间(毫秒): 265.27
每次迭代末字节平均响应时间(毫秒): 265.27
测试中的唯一请求数: 1
唯一响应代码数: 1
错误计数
HTTP:
DNS:
套接字:
其他网络统计数据
平均带宽(字节/秒): 6,819.44
发送字节数(字节): 291,956
接收字节数(字节): 1,753,876
发送字节平均速率(字节/秒): 973.19
接收字节平均速率(字节/秒): 5,846.25
连接错误数:
发送错误数:
接收错误数:
超时错误数:
响应代码
Response Code: 200 - 请求已成功完成。
计数: 1,124
百分比(%): 100.00
|
Normal DataSet |
Typed DataSet |
隐藏结果 |
|
小结
现在,你应该对Typed DataSet有一点了解了,您也明白了它的优势所在,剩下的就是您在架构程序时的决策了。
有些朋友认为没有必要使用Typed DataSet,因为带来了更复杂的结构和更多的代码,也有一些朋友认为Typed DataSet的性能更差或者没有改善。这里想说的就是:性能在某些环境下无关紧要,而且另一些环境下却是产品的最重要特性。过早的优化是一切问题的根源。但是,不重视效率也会导致同样的结果。您是专业人士,是艺术家,是能工巧将。那么,您一定要知道事物的开销。如果您不知道或即使您认为自己知道,也要经常进行测量。
参考资料
J.D. Meier, Srinath Vasireddy, Ashish Babbar, and Alex Mackman, Improving .NET Application Performance and Scalability,MSDN
HOW TO: Create and Use a Typed DataSet by Using Visual C# .NET, MSDN
|