数据库-ADONET-使用强类型DataSet
使用强类型DataSet对象
使用ADONET访问DataSet内容的方式,与使用ADO和DAO的Recordset对象具有类似的编程格式。
l ADONET和VBNET
txtCompanyName.Text=ds.Tables(“Customers”).Row(0)(“CompanyName”)
l ADO、DAP和VB经典方式
txtCompanyName.Text=rs.Fields(“CompayName”).Value
为了更容易的编写数据访问代码,ADONET引入了强类型DataSet对象。现在可以如下编码
txtCompanyName.Text=ds.Customers(0).CompanyName
可以把强类型DataSet认作是一个带有类的DataSet,它是继承自DataSet类的一个子类,并且还包含基于你所指定的架构的属性和方法,同时该类还包含用于DataTable对象及DataRow对象的其他类,这些类可以更高效的编写数据访问代码。
1 创建强类型DataSet对象
如何创建一个强类型DataSet?
有两种办法:其一,使用代码编写并使用NET框架的SDK命令行工具;其二,使用VSNET开发环境。
1.1 使用命令行工具编写代码
NET框架SDK有一个名为XML架构定义工具的命令行使用工具,它能根据XML架构文件(.xsd)生成类文件。
使用这个使用工具,结合DataSet的WriteXmlSchema方法,可以将一个Dataset解析成强类型DataSet类。
1.1.1 使用DataSet对象的WriteXmlSchema方法
XSD文件包含DataSet的架构信息(表、列、约束和关系)。
使用DataSet对象的WriteXmlSchema方法能够创建该文件。
WriteXmlSchema方法,其重载版本能接受,一个Stream对象、一个TextWriter对象、一个XmlWrite对象或字符串表示的文件名。
n 代码演示:用Northwind数据库的Customers和Orders表的列创建一个DataSet,在将DataSet的架构写入文件之前,为两表添加DataRelation。
Dim strConn,strSql as string
strConn=”Providor=SQLOLEDB;Data Source=...;Initial Catalog=Northwind;
Trused_Connection=Yes”
Dim cn As New oleDbConnection(strConn)
strSql=”select CustomerID,CompanyName,ContactName,Phone from Customers”
Dim daCustomers as New oleDBDataAdapter(strSql,cn)
strSql=”select OrderID,CustomerID,EmployeeID,OrderID from Orders”
Dim daOrders as New OleDbDataAdapter(strSql,cn)
Dim ds as new DataSet()
Ds.DataSetName=”Chapter9”
Cn.Open()
daCustomers.FillSchema(ds,SchemaType.Source,”Customers”)
daOrders.FillSchema(ds,SchemaType.Source,”Orders”)
cn.Close()
ds.Relations.Add(“CustomersOrders”,ds.Tables(“Customers”).Columns(“CustomerID”),
ds.Tables(“Orders”).Columns(“CustomerID”))
ds.WriteXmlSchema(“C:"Chapter9.xsd”
1.1.2 使用XML架构定义工具
XML架构定义工具,实际上是指位于Bin目录下的XSD.exe文件,它可以根据XML架构文件生成类文件,还能从Dll文件和EXE文件创建XML架构
l 示例
前面的代码将DataSet架构保存到Chapter9.xsd文件之中,现在在这个基础上使用XML架构定义工具生成一个类文件。
1.开始菜单,NET Framework v2.0,SDK命令提示,
2.对于VBNET输入,XSD Chapter9.xsd /d /l:VB
3.对于C#输入,XSD Chapter9.xsd /d
第一个参数指明XSD文件的路径
第二个参数,指明要创建的类由DataSet派生
第三个参数,指明输出文件语言(默认是C#)
使用时,只需要将新类文件加入“解决方案”,并创建新DataSet类的实例:
Dim ds As New Chapter9()
【类的名字】是基于产生XSD文件的DataSet的名字得来的。
1.2 简单的方法(使用IDE)
创建过程描述:
首先,创建一个所选语言的新Windows应用程序,将OleDBDataAdapter添加到窗体,在适配器向导中,将OleDBDataAdapter指向Northwind数据库,并输入以下语句:
Select CustomerID,CompanyName,ContactName,Phone from Customers
Select OrderID,CustomerID,EmployeeID,OrderDate from Orders
在设计器右键菜单中选择“生成数据集”,输入名字“Chapter9”。
下面设计DataRelation,在“解决方案”窗口,双击架构文件,右键单击Orders表,在菜单中选择“添加”|“新关系”,编辑好关系后确定。
显示类文件
在解决方案窗口顶部,工具栏中选择“显示所有文件”按钮即可。
DataSet(Chapter9.xsd)的架构文件会有两个与该架构文件相关联的文件。第一个是Chapter9.vb或者Chapter9.cs的强类型DataSet类文件;第二个关联文件扩展名为.xsx,它是包含了对XML架构设计器中DataSet的布局进行设置的文本文件。
实际上,该类文件包含许多类。由DataSet派生出来的是主类,这个类公开了两个DataTable对象,分别对应Customers和Orders,而两个DataTable都公开了Item属性,用来返回有DataRow派生出来的子类。
2 使用强类型DataSet对象
强类型DataSet对象简化了开发过程;使编写代码来访问和修改DataSet的内容更加容易。
2.1 添加行
DataSet中的DataTable,每个类都有两种途径——将一个新行添加到DataTable中。
其一:使用New《表名》Row方法,在返回的强类型DataRow中设置列内容,然后调用
Add《表名》Row添加到表。
Dim ds As New Chapter9()
Dim tblCustomers As Chapter9.CustomersDataTable = ds.Customers
Dim rowCustomer As Chapter9.CustomersRow
rowCustomer = tblCustomers.NewCustomersRow
rowCustomer.CustomerID = "ABCDE"
rowCustomer.CompanyName = "ABCDE有限公司"
rowCustomer.ContactName = "ABCED联系人"
rowCustomer.Phone = "(800) 555-1212"
tblCustomers.AddCustomersRow(rowCustomer)
或者这样写:
Dim rowCustomer as DataRow=tblCustomers.NewRow()
rowCustomer(“CustomerID”)=”ABCED”
...
tblCustomers.Row.Add(rowCustomer)
强类型DataRow的优越性之一就是,在IDE中可以直接支持语句完成(Statement Completion)功能。
但是,尽管在VB5开始就已经支持语句完成,但是如果在VB6和ADO2.x代码中输入错误的列名,那么要到运行时才会发现该错误,强类型DataSet和语句完成都无法消除开发时出现的此种问题。
强类型的Add《表名》Row,这个方法也有重载版本。
Dim ds as New Chapter9()
Dim tblCustomers As Chapter9.CustomersDataTable=ds.Customers
tblCustomers.AddCustomersRow(“ABCDE”,”NewCompany”,”NewCantact”,”555-1212”)
上句可替换为
tblCustomers.Rows.Add(new Object(){“ABCDE”,”New Company”,”New Contact”,”555-1212”})
IntelliSense和语句完成是在编写代码时,给人印象最深的特性之一。
2.2 查找行
对于标准非类型化DataSet,可以“根据主键值”,利用DataTable.Rows集合的Find方法,查找特定行。
但是,有时候Find方法会造成混淆,尤其是主键是组合键的情形,例如Northwind数据库中的Order Details表,其主键是OrderID和ProductID。
如果DataTable有一个定义过的主键,那么强类型的DataSet的每一个DataTable都公开自己的Find方法。给强类型DataSet中使用组合键作为主键的表编写查找代码很容易。
daOrderDetails.Fill(Chapter91, "Order Details")
DataGridView1.DataSource = Chapter91.Order_Details
Dim tblDetails As Chapter9.Order_DetailsDataTable = Chapter91.Order_Details
Dim rowDetails As Chapter9.Order_DetailsRow
rowDetails = tblDetails.FindByOrderIDProductID(10248, 59)
If rowDetails Is Nothing Then
MsgBox("行没找到")
Else
MsgBox("找到行!" & vbCrLf & "其订单ID为:" & rowDetails.OrderID & " - " & "产品ID为:" & rowDetails.ProductID)
End If
2.3 编辑行
对强类型DataSet中的行进行编辑的过程与编辑标准数据集中的行类似,
可供选择的方法有BeginEdit、EndEdit和CancelEdit
也可以使用强类型DataRow的属性来访问DataRow列的值。
2.4 处理空数据
可以使用DataRow对象的IsNull函数对空值进行检验;使用System.Convert.DBNull可以给列指派一个空值。
强类型数据集对空值处理是:
每一个强类型的DataRow中,各列都可以:校验列是否包含空值(Is<列名>Null);设置列值为空(Set<列名>Null)
'首先添加数据
daCustomers.Fill(Chapter91, "Customers")
Dim tblCustomers As Chapter9.CustomersDataTable = Chapter91.Customers
DataGridView1.DataSource = tblCustomers
Dim rowCustomer As Chapter9.CustomersRow
rowCustomer = tblCustomers.FindByCustomerID("AROUT")
If rowCustomer Is Nothing Then
MsgBox("没找到")
Exit Sub
End If
'检验公司名空值
If rowCustomer.IsCompanyNameNull() Then
MsgBox("第一次检验- 公司名为空值")
Else
MsgBox("第一次检验- 公司名为: " & rowCustomer.CompanyName)
End If
'设置空值
rowCustomer.SetCompanyNameNull()
'检验公司名空值
If rowCustomer.IsCompanyNameNull() Then
MsgBox("第次检验- 公司名为空值")
Else
MsgBox("第次检验- 公司名为: " & rowCustomer.CompanyName)
End If
2.5 处理层次数据
DataRow对象中,公开的两种通过层次数据进行查找的方法是:GetChildRows和GetParentRow。这两种方法需要提供引用的DataRelation的名字或者其对象本身。
在强类型DataSet中,如果存在DataRelation,则不需要指定DataRelation或其名称,XML架构定义工具将自动添加该方法。
具体的说:在强类型数据集中,给Customers表和Orders表建立关联,将更改保存到xsd文件,xml架构定义工具就会为Customers表添加GetOrdersRows方法到强类型DataRow类中,并为Orders表将GetCustomersRows方法添加到强类型DataRow类中。
Dim ds As Chapter9 = Chapter91
daCustomers.Fill(ds)
daOrders.Fill(ds)
Dim rowCustomer As Chapter9.CustomersRow
Dim rowOrder As Chapter9.OrdersRow
For Each rowCustomer In ds.Customers
ListBox1.Items.Add("客户" & rowCustomer.CompanyName & "的订单")
For Each rowOrder In rowCustomer.GetOrdersRows()
ListBox1.Items.Add(vbTab & "订单编号- " & rowOrder.OrderID & vbTab & "下定日期- " & rowOrder.OrderDate)
Next
Next
‘上面代码对应的非强类型数据集代码是
Dim rowCustomer,rowOrder As DataRow
For Each rowCustomer In ds.Tables(“Customers”).Rows
Console.WriteLine(“Orders for “ & rowCustomer(“CompanyName”))
For Each rowOrder In rowCustomer.GetChildRows(“CustomersOrders”)
...
...
2.6 其他的DataSet、DataTable和DataRow特性
XML架构定义工具生成的类是:DataSet的派生类、DataTable的派生类、DataRow的派生类
因此,可以将它们看做是其非类型化的相应类。
例如:强类型DataSet没有可读写XML数据和架构信息的方法,但是由于派生关系,仍然公开了ReadXML和WriteXml等方法。
对于像用DataAdapter获取数据或提交更改之类的任务,可将强类型DataSet等同于普通DataSet对待。
3 何时使用强类型DataSet
3.1 软件组件
简单的组件,通常比那些提供一大串功能的组件运行的快。
强类型的DataSet,明显比非类型化的DataSet公开的功能要强大。但是,如果不使用附加配件的话,构造简单的,会更好。
3.2 设计时的优势
强类型DataSet在设计时的优势:编写访问代码很容易(这要感谢IDE的语句完成和智能感知IntelliSense功能)。
因为,强类型DataSet的初始化代码中,包含了创建架构和必要的DataTable、DataColumn、DataRelation以及Constraint所需的代码。
使用非类型化DataSet,有三种“将架构信息添加到DataSet的方法”:
1. 自己编写代码;
2. 使用DataSet对象的ReadXmlSchema方法,从XSD文件中加载架构;
3. 使用DataAdapter对象的FillSchema方法
如果正在建立一个使用数据绑定的Windows或web应用程序,那么,使用强类型数据集可以,更容易的在设计时绑定组件。为什么?因为,强类型DataSet包含自己的架构信息,所以能够给一列绑定控件。
创建多层应用程序时,可以通过强类型DataSet,将一个引用添加到一个类库,或将其添加到一个返回强类型DataSet的Web服务是,项目会复制XSD文件和强类型DataSet的类文件,客户端应用程序就能利用强类型DataSet在设计时的优势。
3.3 运行时优势
强类型DataSet对象的运行时含义是什么?它们如何影响应用程序的性能?
使用强类型DataSet对象,不仅易于编写代码,而且,代码还能提高应用程序的性能。
强类型代码,运行速度几乎是非类型化代码的,两倍。
强类型DataSet是如何提高性能的?DataRow对象允许通过提供列的名称、列的序号或DataColumn对象本身来访问列的内容。使用真正DataColumn对象的代码性能最好;使用列的名称编写代码最容易,但是性能最差。
由XML架构定义工具生成的代码兼顾了两方面的优点。编写的代码容易维护,并且自动生成的代码使用了DataColumn对象。
任何程序员无法编写的代码,对于强类型DataSet对象来说,也同样无法生成。
实际上,所有强类型DataSet能实现的功能,都可以编码实现。
比如:要想以访问强类型DataSet那样的性能,访问一个非类型化DataSet的内容,就要避免在集合中执行基于字符串的查找,而使用基于索引的查找,或维护非类型化DataSet中的列引用。
修改代码:在非类型化DataSet中使用适当的DataColumn对象访问行的内容,并使用适当的类型转换代码,结果是新改进的访问比访问强类型DataSet的代码快5%到10%。
由于创建、填充和访问强类型DataSet需要更多的时间,所以这里非类型化DataSet比强类型DataSet性能上稍微好一点。
3.4 其他注意事项
决定使用强类型DataSet,还需要考虑其他一些问题。
3.4.1 进行结构更改
如果,需要通过添加或修改DataColumn对象来更改强类型DataSet对象的结构,则,必须重新生成强类型DataSet对象,特别是在生成一个多层应用程序(并且中间层返回强类型DataSet)时。
如果重新生成了强类型DataSet(中间层要返回的),就需要在对中间层的引用进行刷新之后,重新生成客户端程序。
但是,如果准备改变服务器返回的数据结构时,不论是否使用强类型DataSet,都需要改变访问该结构的客户端代码。
3.4.2 转换DataSet对象
由于强类型DataSet继承自标准DataSet,因此,通过“一个非类型化DataSet接口访问强类型DataSet的代码”是正确的。
比如:
Dim dsStrong as New Chapter9()
Dim dsUntyped as DataSet
dsUntyped=Ctype(dsStrong,DataSet)
但是,还要特别注意这个问题:
Dim dsStrong1,dsStrong2 as Chapter9
Dim dsUntyped as DataSet
‘下面是能成功的代码
Strong1=new Chapter9()
dsUntyped=Ctype(dsStrong1,DataSet)
dsStrong2=Ctype(dsUntyped,Chapter9)
‘下面的代码则会抛出异常
dsUntyped=New DataSet()
dsStrong2=Ctype(dsUntyped,Chapter9)
如果,有一个非类型化的DataSet,想使用强类型DataSet访问它,该怎么办?
(这里说的是,原有DataSet是非典型的DataSet)
可以使用强类型DataSet的Merge方法,从非类型化DataSet中导入数据。
Dim dsStrong as New Chapter9()
Dim dsUntyped as New DataSet()
dsStrong.Merge(dsUntyped)
Merge方法,有助于在两个不同的“强类型DataSet”实例之间,转移数据。
如果对WriteXml和ReadXml方法的调用,包含Xml架构的话,还可以使用WriteXml和ReadXml方法在两个不同的强类型DataSet类实例之间转移数据。
3.4.3 强类型DataSet对象的非类型化特性
假如,应用程序使用强类型DataSet,并且想要将DataSet送回中间层服务器,然后将更改提交给数据库的话,可以使用强类型DataSet对象提供的GetChanges方法,来创建一个只包含被修改的行的新DataSet,并且,GetChanges方法返回非类型化DataSet。
能否将GetChanges方法返回的非类型化DataSet,再转化成强类型DataSet呢?当然是可以的。
Dim dsStrongAllRows as New Chapter9()
‘对强类型进行填充,并修改一些行,此处代码省略
Dim dsUntyped as DataSet
dsUntyped=dsStrongAllRows.GetChanges()
Dim dsStrongModifiedRows as Chapter9
dsStrongModifiedRows=Ctype(dsUntyped,Chapter9)
强类型DataSet,还有其他返回非类型化数据的方法,比如,Select方法,返回DataRow对象的一个数组,不能将返回数组转化为强类型DataSet对象的数组,但是可以将单个DataRow对象转化为相应的强类型DataRow。
相似的规则也可用于DataView,虽然不能直接通过强类型DataSet类访问它的内容,但是能够将通过DataRowView的Row属性返回的DataRow转化为强类型的。
Dim dsStrong as New Chapter9()
‘对此对象填充数据,和修改部分行,代码略
Dim vueCustomers as New DataView(dsStrong.Customers)
Dim rowCustomer as Chapter9.CustomersRow
rowCustomer=Ctype(vueCustomers(0).Row,Chapter9.CustomersRow)
3.5 方法的选择
什么样的选择才是正确的?
强类型DataSet,能够更快的创建应用程序,能够更容易的编写高效的代码。
但是它们不提供最好的性能。
如果结合非类型化DataSet和智能代码来使用,就可以创建运行更快的程序。
选择,完全取决于程序的需要。
如果程序的性能不是最优先的考虑事项,应该使用非类型化DataSet对象;但是如果牺牲一部分性能来换取开发时间是值得的,那么就应该考虑使用强类型DataSet。