ADONET-使用DataSet处理脱机数据

 

处理脱机数据——用DataSet对象

1          DataSet的特性

DataSet是数据的集合。可以把它看做Excel的工作簿,里面保存了多个工作表(查询结果)。

l        DataSet对象与DataReader对象的区别?

DataReader是为速度而创建的,所提供的功能有限。DataReader中的数据是只读的,并且DataReader一旦移动到下一行,就不能返回查看前面的行(单向的)。

DataSet是为处理脱机数据而建的,提供了很多强大的功能。

1.1         处理脱机数据

DataSet中的数据是与数据库断开连接的。(一旦用DataAdapter将查询结果返回DataSet——填充DataSetDataSet和数据库之间便不再有连接。)

脱机处理数据的好处。最大的好处是不需要始终与数据库连接。

DataSet的脱机数据机制对创建多层应用程序很有用,可以将DataSet的内容从一个组件传递到另一个组件;接收数据的组件可以像XML文档那样处理信息;当该组件是用.NET框架创建之时,可以将信息作为一个DataSet来处理。

1.2         浏览、排序、搜索、过滤

DataSet对象可以在任何时候查看其中的任意行的内容,

DataSet对象还允许修改查看查询结果的方式。(可以将其中的数据根据一列或几列进行排序;根据简单搜索条件查找一行数据;对其中的数据进行过滤)

1.3         处理分级数据

DataSet对象设计上就是用来处理分级数据的。

DataSet对象允许定义其中所存储的多个数据表之间的关系。

1.4         缓存更改

DataSet对象允许缓存一行数据的更改,之后可以使用DataAdapter对象将这些更改提交给数据库;

还可以通过检查DataSet中修改过的行,比较每一行的原始值和当前值,来判断对这些行作出过什么修改(插入、修改、删除)

1.5         XML的完整性

通过创建ADONETDataSet对象来处理XML,可将DataSet对象的内容保存为XML文档的文件或将XML文档中的数据加载到DataSet对象中;DataSet对象还可以将表、列、约束等架构信息分离到XML架构文件中。

ADO.NET中,DataSet对象和XML文档几乎是可以互换的。这种二元性允许开发者使用这一最方便的接口(XML开发者可以用DataSet对象处理XML文档;数据库开发者可以像处理DataSet那样处理XML

1.6         统一功能

ADORecordSet对象与ADONETDataSet对象功能上很相似。

2          使用DataSet对象

DataSet对象和它的子对象:

l              DataSet包含DataTableDataRelation

l              DataTable包含DataRowDataColumn和约束对象。

2.1         创建数据集对象

DataSet对象的一个可选构造函数中,可以设置数据集的名字属性(DataSetName属性)

l              代码演示

Dim ds as new DataSet()

Dim ds1 as new DataSet(“DataSetName”)

l              所属名字空间

DataSetDataTableDataColumnDataRowConstraintDataRelation都驻留在System.Data名字空间中。

2.2         查看调用DataAdapter.Fill()创建的结构

l              准备工作

Dim StrConn,strSql as String

strConn=”Provider=SQLOLEDB;Data Source=(local)"???;Initial Catalog=Northwind;Trusted_Connection=Yes”

strSql=”select CustomerID,CompanyName,ContactName,Phone from Customers”

Dim da as new OleDbDataAdapter(strSql,strConn)

Dim ds as new DataSet()

da.Fill(ds,”Customers”)

在查看查询结果之前,先分析一下DataAdapter创建的用于存储结果的那些结构。

2.2.1            DataTable对象

DataTable是为了更稳定的数据而设计的,可以对DataTable中的数据进行修改、排序以及过滤处理。

DataTable对象有一个Column属性用来返回DataColumn对象的集合,各个DataColumn对应于查询结果中的列。

PSDAOADORecordSet对象均包含Fields属性(一个Field对象的集合)

2.2.2            DataColumn对象

DataColumn对象用于定义DataTable的模式。

当使用DataAdapter.Fill()填充DataTable时,DataAdapter对象为查询结果中的每一列创建一个DataColumn对象。(此对象设置了最基本的属性:NameOrdinalDataType

n         代码演示:显示Fill方法创建的DataColumn对象的基本信息

连接数据库部分略

Dim da as new DataAdapter(sSql,sConn)

Dim ds as new DataSet()

da.Fill(ds,”Orders”)

Dim tbl as DataTable=da.Tables(0)

Debug.WriteLine(“Column信息:表名=” & tbl.TableName)

Dim Col as DataColumn

For Each col in tbl.Column

       Debug.WriteLine(vbTab & col.ColumnName & “ – “ & col.DataType.toString)

              Next Col

2.3         查看DataAdapter返回的数据

ADO.NETDataTable对象,更符合XML文档,它可树到任意节点。再使用DataTable的情况下,所有的行在任意时间都是可用的。

DataTable类公开了Rows属性,该属性返回DataTable中可用到DataRow对象集合。

2.3.1            DataRow对象

DataRow对象允许查看并修改DataTable中的行。

可以用DataTable.Rows属性将一个DataRow对象指派给DataTable中的特定行。

DataTable.Rows属性返回一个包含DataRow集合的DataRowCollection对象。

n         代码演示:用DataAdapter.Fill方法将查询结果填入DataTable,将第一行分配到DataRow对象并显示其中两列的内容

Dim strConn,strSql as string

strConn=””

strSql=”select OrderID,CustomerID,EmployeeID,OrderDate from Orders”

Dim da as new OleDbAdapter(strSql,strConn)

dim ds as new DataSet()

da.Fill(ds,”Orders”)

 

Dim tbl as DataTable=da.Tables(0)

Dim row as DataRow=tbl.Row(0)

Console.WriteLine(“OrderID=” & row(“OrderID”) & vbTAB & “CustomerID=” & row(“CustomerID”))

n         PSDataRow对象包含返回指定列内容的参数化Item属性。可以像上述代码那样提供列的名称,或者,提供列中DataTable中的序数位置。

n         PS2:在使用DataReader的情况下,使用基于索引到查询比使用基于字符串的查询要更快的返回数据。

2.3.2            检查存储在DataRow中的数据

如果想要辨别一个更一般的历程来显示DataRow中的内容应该怎么做?

假设你是想编写一个接受DataRow对象的,并显示DataRow中列名称和值的过程。

u       若是使用DataReader对象,可以通过检查它的FieldCount属性来确定列的数量,然后用GetNameItem来取得每一列的名称和值。

然而,DataRow中并没有与DataReader对象FieldCount属性相对应的属性。

u       DataRow对象公开了Table属性,其中包含了DataRow所在的DataTable

通过此属性,在返回的DataTable中获取列的总量和各列的名称信息。

n         代码演示:使用DataRowTable属性来显示DataRow的内容。

Private Sub DisplayRow(ByVal row as DataRow)

       Dim tbl as DataTable=row.Table

       Dim col as DataColumn

       For Each col In tbl.Columns

              Console.WriteLine(vbTab & col.ColumnName & “ – “ & row(col))

从这里看rowitem属性可以接受DataColumn类型参数

       Next col

End Sub

DataRow对象的Item方法能接受一个DataColumn对象,以便查看特定列的内容。(第三种方法)

通过提供DataColumn来取得行的内容比基于系数的查询性能更好(好6%

2.3.3            检查DataTable中的DataRow对象

开发者可以像浏览NET框架中的任意集合一样的来回浏览DataTable中的DataRow对象。

可以使用的语句:For循环或For Each循环语句。

n         代码演示:浏览DataTable中的内容(依赖前面代码的DisplayRow过程)

Dim sConn,sSql as String

sConn=””

sSql=”select OrderID,CustomerID,EmployeeID,OrderDate from Orders”

Dim da as New OleDbDataAdapter(sSql,SConn)

Dim ds as new DataSet()

da.Fill(ds.”Orders”)

Dim tbl as DataTable=ds.Table(0)

Dim col as DataRow

Dim i as Integer

For Each row In tbl.Rows

       i+=1

       console.WriteLine(“Contents of row #” & i)

       DisplayRow(row)

Next row

2.4         校验DataSet中的数据

一般,数据库都提供了不同的机制用来确保数据库中的数据的正确性。

例如:Northwind数据库就定义了很多规则和约束:填充Customers表中CustomerID列的字符串不能超过5个字符,且值必须唯一;Orders表自动为每一行生成OrderID,且每行的CustomerID必须对应到Customers表的一个现有项。

也许,在你提交更改之前,需要应用类似的规则校验数据。

但是,由于数据库很可能已经定义了类似的校验规则,这种想法很可能有点多余。但是,在应用程序中添加校验规则可以增强它的性能;另一个好处是减少网络流量和数据库的负载。

ADO.NETDataSet提供了许多中数据库系统中可用的、相同的数据校验机制,可以将这些校验机制(或者叫约束)分成两类:列级限制以及表级限制。

2.4.1            校验DataColumn的属性(列级限制)

DataColumn对象用于校验数据的属性。

l              ReadOnly(只读性)

保证数据正确性的最简单办法(不让用户修改它)。

l              AllowDBNull(允许空)

Boolean类型。

l              MaxLength(最大长度)

对列中字符串的大小的限制。

l              Unique(唯一性)

若值重复,会抛出ConstrainException异常。

2.4.2            DataTable对象的Constrains集合

ADO.NET对象模型包含两个类:UniqueConstrainsForeignkeyConstraint。它们都是由Constraint类派生而来。

DataTable提供的Constraints属性,可以——添加、修改、查看——位于DataTable上的约束。

2.4.2.1      UniqueConstraints

若数据列的Unique属性被设为True(该列有唯一性),同时,还会中DataTable的约束集合中添加一个UniqueConstraints对象。

PS:在DataColumn中设置Unique,要比,中DataTable的约束集合中创建一个新的UniqueConstraints简单得多。但是,有时候还是需要第二个办法的,比如:需要确定多列合并之后的值是否惟一的时候。

2.4.2.2      Primarykey

主键(Primarykey)是UniqueConstraints的一种特殊类型。

ADONET对象模型中的DataRowCollection对象可以用Find方法来根据主键列的值查找DataTable中的行,好像这样:row=myTable.Rows.Find(“ALFKI”)

注意:数据表可以有多个唯一性约束,但是只能有一个主键。

设置主键,可以用,PrimaryKey属性。

2.4.2.3      ForeignkeyConstraints

外部约束(ForeignkeyConstraints)可以被引入到DataTable中。

例如:前面提到Orders表中CustomerID必须与Customers表中的CustomerID对应。这可以通过创建一个ForeignkeyConstraint对象,并将它添加到DataTable中来对DataSet中的数据进行限制。

PS:通常不必去特意创建ForeignkeyConstaint,因为当DataSet中的两个DataTable之间建立起DataRelation后,会自动创建ForeignkeyConstraint

注意:ADONET并不知道数据库中保存了哪些数据。因此中DataSet中的列和约束只能中该DataSet中有效。

2.4.3            DataAdapter.Fill模式来检索模式信息

首先:验证数据是需要时间的。

因此,许多方案都没有对DataSet设置验证属性,除非,有明确的要求。

在内部,DataAdapterFill方法,在创建DataTable时,也不会对DataColumn进行验证属性方面的设置,并且,不会将约束添加到DataTable对象的约束集合。

在填充DataTable过程中,可以使用两种办法,通知DataAdapter在数据库中检索其模式信息:

u       通过DataAdapter对象的MissingSchemaAction属性,将它设置为AddWithKey

u       调用DataAdapter对象的FillSchema方法

注意ADONET有一些功能应该尽量避免中最终应用程序中使用。而,通过DataAdapter对象的上面两种方法获取DataSet的模式信息,就是应该极力避免的行为。

使用DataAdapter获取模式信息,能在设计时节省时间(事实是,VSNET使用DataAdapter中设计时产生了DataSet对象)。

如果只是创建小型示例或者概念示例,都无话可说,的确能减少代码的编写量。但是,一般的情况时,你不需要知道返回的是哪一列,因此,完善的应用程序中,像DataAdapter.FillSchema这样的功能禁止使用。

使用上述两种方法(DataAdapter特性)检索模式信息,会导致:

u       查询它所创建的每一个新DataColumn的名称和数据类型

u       DataAdapter中数据库中查询模式信息(查看数据列对象的ReadOnlyAllowDBNullMaxLengthUnique属性是否都被正确设置)

u       DataAdapter也将设置新的DataColumn对象的AutoIncrement属性

DataAdapter将,试图,为DataTable生成一个主键,这一点相当糟糕。原因是:

DataAdapter必须先查询数据库,以便确定查询所引用的表,然后,再一次查询数据库来收集该表主键的信息。若没有,DataAdapter将请求得到该表惟一索引的信息,若该表包含有一个包括两个列的主键,而,所使用的查询却不包含这些列,DataAdapter将不再使用DataTable中的这个主键。

2.5          编写代码成绩DataTable对象

前面学过的内容包括:

ü 使用DataAdapterFillFillSchema方法创建DataTable对象

ü 当想要用列级或者表级限制来验证数据时,应该创建自己的DataTable对象

2.5.1     创建DataTable对象

代码举例:

Dim tbl as new DataTable(“TableName”)

Console.WriteLine(tbl.TableName)

2.5.2     DataTable添加到DataSet对象的Table集合

可以使用DataTableCollection对象的Add方法将一个已存在的DataTable对象添加到现有的DataSet对象当中.代码如下:

Dim ds as new DataSet()

Dim tbl as new DataTable(“Customers”)

ds.Tables.Add(tbl)

代码也可以进行以下简写:

Dim ds as new DataSet()

Dim tbl as DataTable=ds.Tables.Add(“Customers”)

通过查看DataTable对象的DataSet属性,可以确定它是否存在于DataSet(返回所在的DataSet对象;如果没有则返回Nothing或者Null).这是个只读属性.

DataTable对象只能属于一个DataSet对象(或者没有).因此,如果希望多个DataSet都含有它,必须使用Copy方法或者Clone方法来复制副本.

Copy方法,能创建一个与原型结构相同,并且包含相同行的新DataTable

Clone方法能够创建一个与原型结构相同,但是不包含任何行的新DataTable

2.5.3     DataTable添加列

为了保存查询的结果,DataTable必须包含行.

[前面提到过]DataAdapter是如何创建新的DataColumn对象

A.        先创建一个DataColumn对象

B.        然后用代码加入到Table对象的列集合中.

C.        代码样例:

Dim da as New DataSet()

Dim tbl as DataTable=da.Tables.Add(“Customers”)

Dim col as DataColumn=tbl.Columns.Add(“CustomerID”)

2.5.4     指定DataColumn的数据类型

创建新DataColumn之后,还需要指定其所包含的数据的类型.

使用DataColumnDataType属性,可以设置或查看列的数据类型.

但是,如果已经将数据添加到DataTable对象的Rows集合之后,,DataColumn对象的DataType属性将不可读写.

DataColumn的数据类型依赖于数据库中该列的数据类型

数据库中的数据类型跟DataColumn的数据类型不是一对一的关系.

Sql Server中,就字符串而言,它的存储方式有:定长或变长。数据可以是单字节或双字节。

然而,对于ADO.NET,以上的类型在DataColumn中都被视为字符串。

DataColumnDataType属性只处理.NET类型,不处理数据库的数据类型。

DataColumnDataType属性默认类型是字符串。

DataColumn对象的一个构造函数可以指定其数据类型和名称。

同时,DataColumnCollection.Add方法的一个重载版本也可以运行为新DataColumn对象指定ColumnNameDataType属性。

代码示例

Dim ds as new DataSet()

Dim tbl as DataTable=ds.Tables.Add(“Orders”)

Dim col as DataColumn=tbl.Columns.Add(“OrderID”,GetType(Integer))

DataType属性的类型是Type。注意:VBNETC#使用了不同的函数来生成类型,在VBNET中是GetType函数;在C#中是typeof运算符。

2.5.5     添加主键

注意:用代码设置DataColumnDataTable的验证特性是应该提倡的;而不要用DataAdapter去查询数据库的约束。

代码设置验证特性,通过数据列DataColumn对象的四大属性:AllowDBNullReadOnlyMaxLengthUnique

代码演示:

Dim ds as new DataSet()
Dim tbl as DataTable=ds.Tables.Add(“Customers”)

Dim col as DataColumn=tbl.Columns.Add(“CustomerID”)

col.AllowDBNull=False

col.MaxLength=5

col.Unique=True

col.ReadOnly=False

设置DataTable对象的主键,稍微复杂。其PrimaryKey属性包含一个DataColumn对象的数组,所以不能只是简单的将列的名字设置为主键。

DataTable对象的主键设置分为:用单一列作主键;和使用列组合作主键,两种。

无论哪种,都要先创建一个DataColumn对象数组,再将该数组赋值给DataTable.PrimaryKey属性。

代码演示:第一种,单一列主键

Dim ds as new DataSet()

With ds.Tables.Add(“Customers”)

      .Columns.Add(“CustomerID”, GetType(String))

      .PrimaryKey=New DataColumn(){.Column(“CustomerID”)}

End With

第二种,组合列主键

...

With ds.Tables.Add(“Order Details”)

      .Column.Add(“OrderID”,GetType(Integer))

      .Column.Add(“ProductID”,GetType(Integer))

      ...

      ..PrimaryKey=new DataColumn(){.Columns(“OrderID”), .Columns(“ProductID”)}

End With

值得注意的是,代码设置主键之后,ADONET将自动把主键列对象的AllowDBNull设为False

2.5.6     添加其他约束

除了主键约束之外,还有惟一键外键约束,他们都可以添加到DataTable中。

DataTable对象的约束集合(Constraints)有一个Add的重载方法,它的参数可以用来添加新的主键、惟一键、外键约束。

演示代码

代码说明

根据CustomerID列将一个惟一键添加到客户表,根据OrderIDProductID两列将一个惟一键添加到订单明细表,同时将一个外键约束添加到订单明细表(确保OrderID的值与订单表中对应的行匹配)

提示

创建约束有两个办法:

第一:直接创建新的约束对象并添加到表对象约束集合。

tbl.Constraints.Add(New UniqueConstraint(...))

第二:使用约束集合对象的Add重载方法,创建新约束并加入集合。

tbl.Contraints.Add(“constraint_name”,ColumnInformation)

推荐:使用 第一种 方法,因为它容易理解,尽管Add方法可以重载。

代码演示

Dim ds as New DataSet()

With ds.Tables.Add(“Customers”)

      .Columns.Add(“CustomerID”, GetType(Integer))

      ...

      .Constraints.Add(New UniqueConstraint(.Columns(“CustomerID”))’将客户ID添加为惟一键

      (.Constrains.Add(“UK_Constomers”, .Columns(“CustomerID”),False))’第二种写法

End with

 

创建订单详细信息的数据表

With ds.Tables.Add(“Order Details”)

      .Columns.Add(“OrderID”,GetType(Integer))

      .Columns.Add(“ProcedureID”,GetType(Integer))

      ...

      Dim Cols(1) as DataColumn

      Cols(0)=.Columns(“OrderID”)

      Cols(1)=.Columns(“ProcedureID”)

      .Constaints.Add(New UniqueConstraint(Cols))’这里使用组合键设置惟一键

      (Constaints.Add(“UK_Order Details”,Cols,False))’第二种办法

     

      ‘添加外键

      .Constaints.Add(New ForeignKeyConstaint(ds.Tables(“orders”).Columns(“OrderID”),_

                                           .Columns(“OrderID”)))

      (.Constaints.Add(“FK_Order Details_Orders”, ds.Tables(“Orders”).Columns(“OrderID”),_

.Columns(“OrderID”))))’第二种写法

End with

2.5.7     处理自增列

ADONET对象模型通过DataColumn对象的三个属性来实现(支持)自动增量列,它们是:

l      AutoIncrement

l      AutoIncrementSeed

l      AutoIncrementStep

将数据列对象DataColumnAutoIncrement属性设置为True,就可以为数据表的新行生成自动增量值。

演示代码

Dim ds as new DataSet()

Dim tbl as DataTable=ds.Tables.Add(“Orders”)

Dim col as DataColumn=tbl.Columns.Add(“OrderID”,GetType(Integer))

Col.AutoIncrement=true

Col.AutoIncrementSeed=-1

Col.AutoIncrementStep=-1

Col.ReadOnly=true

建议

无论何时设置AutoIncrement=True之后,都要记住将AutoIncrementSeedAutoIncrementStep设置为-1

原因如下:

AutoIncrementSeedAutoIncrementStep属性控制ADONET如何生成新值。

当处理空表时,ADONET会将存储在AutoIncrementSeed的值赋给第一行自动增量列;

接着ADONET将使用AutoIncrementStep属性生成后续的自动增量值。

例如:

如果

AutoIncrement=True

AutoIncrementSeed=2

AutoIncrementStep=2

那么ADONET为前5行产生的自增量为:246810

使用DataAdapter.Fill填充DataTable的处理方式稍有不同。

如果将AutoIncrement=True

AutoIncrementSeed=5

AutoIncrementStep=5

当数据表为空时,新增行的自增列值依次为5101520...

如果用DataAdapter填充数据表,并用DataTable.Rows.Add添加新行,列的新值将取决于从数据库取得的数据。然后,ADONET会根据数据表中出现的最大自增量和AutoIncrementStep属性的值生成后续值。

特别注意

ADONET产生自增量最大值的根据是查询的结果(若查询结果只是特定的客户名单,最大值也是根据这个结果产生的)也许数据库已经存在ADONET生成的新值。这一点要特别注意。

 

ADONETNET框架的整体开发中,可以依靠ADONET的自动增量功能,在将行添加到DataTable过程中,让ADONET对行计数,从而达到分页显示DataTable的目的(而不用根据查询规则和行的排序来实现分页)。

自动增量的“可为”与“不可为”

可为:使用ADONET自动增量特性

不可为:将ADONET生成的自动增量值,提交给数据库。(该值仅为占位符,数据库会生成真正的新值)

不可为:显示未提交给数据库的,新行的,自动增量值。(数据库可能会根据ADONET产生的增量值生成不同的值)

可为:将AutoIncrementSeedAutoIncrementStep属性值设置为-1,确保生成的占位符的值不会出现在数据库中。

代码示例

说明:演示根据简单查询的结果填充DataTable

填充之前,先添加新的自动增量列。

填充之后,ADONET会为每一行填充生成新的自增值。

        Dim strConn As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=。。。"

        Dim strSql As String

 

        strSql = "select 客户ID,公司名称,联系人姓名 from 客户"

        Dim da As New OleDbDataAdapter(strSql, strConn)

Dim ds As New DataSet()

        da.Fill(ds, "客户")

        DataGridView1.DataSource = ds.Tables("客户")

 

        da.FillSchema(ds, SchemaType.Source, "客户2")

        Dim tbl As DataTable = ds.Tables("客户2")

        Dim col As DataColumn = tbl.Columns.Add("行号", GetType(Integer))

        With col

            .AutoIncrement = True

            .AutoIncrementSeed = 1

            .AutoIncrementStep = 1

        End With

        da.Fill(ds, "客户2")

 

        Dim dgv2 As DataGridView

        dgv2 = New DataGridView()

        Me.Controls.Add(dgv2)

        With dgv2

            .Top = DataGridView1.Top + DataGridView1.Height + 10

            .Left = DataGridView1.Left

            .Height = DataGridView1.Height

            .Width = DataGridView1.Width

            .Show()

        End With

        Me.Height = dgv2.Bottom + 100

 

            dgv2.DataSource = ds.Tables("客户2")

2.5.8     添加基于表达式的列

数据库管理员通常会避免:在数据库中,包含,由现有数据派生出来的数据。

大多数数据库的查询语言都支持表达式,在查询结果中,可以包含被统计的列。

例如:select OrderID,ProductID,UnitPrice,Quantity,UnitPrice*Quantity as ItemTotal from [Order Details]

如果将查询结果添加到数据表中,就会看到有一列包含了表达式计算结果。

但是,修改数据表中的UnitPriceQuantity列,不会引起计算列的变化。

ADONET允许创建基于表达式的DataColumn对象,将DataColumnExpression属性设置为一个表达式。

当查看列内容的时候,ADONET就会计算表达式并返回结果;修改也会反映到计算列中。

演示

        Dim str_conn As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=。。。"

        Dim str_sql As String = "SELECT 产品.产品名称, 订单明细.单价, 订单明细.数量, 订单明细.折扣 FROM (订单明细 INNER JOIN 产品 ON 订单明细.产品ID = 产品.产品ID)"

        '第一步我要取表结构,然后添加新列

        Dim da As New OleDbDataAdapter(str_sql, str_conn)

        Dim ds As New DataSet()

        da.FillSchema(ds, SchemaType.Source, "我的查询")

        Dim tbl As DataTable = ds.Tables("我的查询")

 

        tbl.Columns.Add("总价", GetType(Decimal), "单价 * 数量")

        tbl.Columns.Add("折后价", GetType(Decimal), "总价*(1-折扣)")

 

        da.Fill(ds, "我的查询")

拓展

Expression属性支持许多函数。

包括:能引用数据集中的其它数据表中数据的聚合函数。(处理关系数据时,使用聚合函数创建基于表达式的列)

2.5.9     为客户表、订单表、订单详细信息表创建DataTable对象

说明:把DataSetDataTableDataColumn结合到一个数据集中。

代码示例:

下面的代码创建一个包含3DataTableDataSet(包括DataSet对象的每个部分),并用代码设置DataColumn对象的属性(DataTypeAllowDBNullAutoIncrement),创建主键约束和外键约束。

        Dim col As DataColumn

        Dim fk As ForeignKeyConstraint

        '客户表

        With ds.Tables.Add("客户")

            col = .Columns.Add("客户ID", GetType(String))

            col.MaxLength = 5

 

            col = .Columns.Add("公司名称", GetType(String))

            col.MaxLength = 40

 

            col = .Columns.Add("联系人", GetType(String))

            col.MaxLength = 30

 

            col = .Columns.Add("联系电话", GetType(String))

            col.MaxLength = 24

 

            .PrimaryKey = New DataColumn() {.Columns("客户ID")}

        End With

 

        '订单表

        With ds.Tables.Add("订单")

            col = .Columns.Add("订单ID", GetType(Integer))

            col.AutoIncrement = True

            col.AutoIncrementSeed = -1

            col.AutoIncrementStep = -1

            col.ReadOnly = True

 

            col = .Columns.Add("客户ID", GetType(String))

            col.AllowDBNull = False

            col.MaxLength = 5

            .Columns.Add("员工ID", GetType(Integer))

            .Columns.Add("下定日期", GetType(DateTime))

            .PrimaryKey = New DataColumn() {.Columns("订单ID")}

        End With

        '订单详细信息表

        With ds.Tables.Add("订单明细")

            .Columns.Add("订单ID", GetType(Integer))

            .Columns.Add("产品ID", GetType(Integer))

            .Columns.Add("单价", GetType(Decimal))

 

            col = .Columns.Add("数量", GetType(Integer))

            col.AllowDBNull = False

            col.DefaultValue = "1"

 

            col = .Columns.Add("折扣", GetType(Decimal))

            col.DefaultValue = "0"

 

            .Columns.Add("单项总价", GetType(Decimal), "单价 * 数量 * (1- 折扣)")

 

            .PrimaryKey = New DataColumn() {.Columns("订单ID"), .Columns("产品ID")}

        End With

 

        '外键约束

        fk = New ForeignKeyConstraint(ds.Tables("客户").Columns("客户ID"), ds.Tables("订单").Columns("客户ID"))

        ds.Tables("订单").Constraints.Add(fk)

 

            fk = New ForeignKeyConstraint(ds.Tables("订单").Columns("订单ID"), ds.Tables("订单明细").Columns("订单ID"))

            ds.Tables("订单明细").Constraints.Add(fk)

2.6          修改DataTable内容

2.6.1     添加新DataRow

这里是指一行行的加载数据

每个DataTable对象都有一个Rows属性。用来返回DataRowCollection对象,此对象包含DataRow对象的集合。(多数集合都可以用Add方法将新对象加入集合)

另外,DataTable对象的NewRow方法,将返回新生成的DataRow对象(其中包含了表中的列信息——字段名、类型、可空等等)。

代码:

Dim row As DataRow=ds.Tables(“Customers”).NewRow

row(“”)=”ALFKI”

创建新的DataRow之后,你可以,使用Item属性填充不同的列;还可以用item属性,检查行中列的内容。

Item属性,是DataRow对象的默认属性,因此可以省略掉。(引用DataRow中列的值,需要列名,或者索引)

DataTableNewRow方法,创建一个新行,但不会将该行添加到DataTable中,因为,该行是空行(若列没有默认值,该列就可能是适当的默认值或Null)。所以,可以在此时,给新行中的列赋值。

假如:客户表的CustomerID列不能接受Null值,也没有默认值。如果不给CustomerID列赋值,但是还要将新的DataRow对象敬爱如到表,就会产生异常。

给新行(DataRow对象)所有必须的列赋值,之后,可以使用DataRow Collection对象的Add方法,将新行加入到表(DataTable对象)。

        '客户表

        Dim c_id As String = "ALFKI"

        Dim row As DataRow = ds.Tables("客户").NewRow

        row("客户ID") = c_id

        row("公司名称") = "美联邦"

        row("联系人") = "奥巴马"

        row("联系电话") = "2000302"

        ds.Tables("客户").Rows.Add(row)

 

        '订单表

        row = ds.Tables("订单").NewRow

        row("客户ID") = c_id

        row("员工ID") = 821

        row("下定日期") = Now

        ds.Tables("订单").Rows.Add(row)

 

        Dim o_id As Integer = ds.Tables("订单").Rows(0).Item("订单ID")

 

        '订单明细

        row = ds.Tables("订单明细").NewRow

        row("订单ID") = o_id

        row("产品ID") = 8541

        row("单价") = 45.25

            ds.Tables("订单明细").Rows.Add(row)

另外:数据表对象(DataTable)提供了另外一种方法——将新行添加到表——LoadDataRow方法。

此方法,第一个参数是一组数值,数组中的项与表中列相对应;

参数AcceptChanges,用来控制新行DataRowRowState属性值。若设置为False,则新行的RowState属性值为Added(跟使用DataTable.NewRowRows.Add组合添加行——效果相同)。

        Dim avalues As Object() = {"BFKDL", "民主党", "麦凯恩", "030-0074321"}

        ds.Tables("客户").LoadDataRow(avalues, False)

当调用DataAdapter对象的Update方法,将更改提交给数据库时,DataAdapter会检查每个DataRowRowState,确认如何更新数据库{修改现有行、添加新行、删除现有行}

AcceptChanges参数设置为True,则新DataRowRowState值为Unmodified,表示该行不包含DataAdapter要提交给数据库的挂起更改。

2.6.2     修改当前行

有三种办法可以修改行的内容。

最简单办法

一旦有DataRow之后,你就可以使用DataRow对象的Item属性,对列赋值。

        Dim ds As DataSet

        Dim tbl As DataTable

        tbl = DataGridView1.DataSource

        ds = tbl.DataSet

 

        Dim rowCustomer As DataRow

        rowCustomer = ds.Tables("客户").Rows.Find("ANTON") '记住:Find方法的参数,只能是主键值(可以是组合值)

        If rowCustomer Is Nothing Then

        Else

            rowCustomer("公司名称") = "魔梦"

            rowCustomer("联系人姓名") = "我不活了"

        End If

使用DataRow对象的一对方法:BeginEditEndEdit

        Dim r As DataRow

        Dim tbl As DataTable

        tbl = DataGridView1.DataSource

        Dim ds As DataSet

        ds = tbl.DataSet

 

        r = ds.tables("客户").rows.find("ANTON")

 

        If (r Is Nothing) Then

        Else

            r.BeginEdit()

            r("公司名称") = "美联储"

            r("联系人姓名") = "奥巴马"

            r("联系人头衔") = "没好得瑟"

            r.EndEdit()

        End If

使用BeginEditEndEdit组合,可以缓存对行的修改。

调用EndEdit可以保存对行的修改;如果不想保存更改,可以调用CancelEdit取消修改,行会返回到调用BeginEdit时的状态。

这两种修改行的方法,不同之处,在于:调用会引发DataTable的一些特殊事件(比如RowChangingRowChangedColumnChangingColumnChanged

方法一,每次修改行中列值,行的内容都改变。每次修改都会激发DataTable对象的事件

方法二,使用BeginEdit可以在调用EndEdit之前阻止事件激活。

第三种修改行的方法

使用ItemArray属性(可以检索或修改行的内容)

Item属性的不同之处在于,ItemArray可以接受对应列的一组值。

        Dim tbl As DataTable

        tbl = DataGridView1.DataSource

        Dim ds As DataSet

        ds = tbl.DataSet

 

        Dim r As DataRow

        r = ds.Tables("客户").Rows.Find("ALFKI")

 

        Dim values As Object() = {"ALFKI", "太扯淡公司", "克林顿夫妇", "892302323"}

        r.ItemArray = values

使用VBNET关键字Nothing或者C#关键字Null,可以跳过不想修改的列。

Dim values As Object() = {Nothing, "太扯淡公司", "克林顿夫妇",Nothing}

注意

修改行的内容并不会,自动修改数据库中的相应内容。

对行所做的修改,被视为随后将使用DataAdapter对象提交给数据库的,待定更改。

推荐使用BeginEditEndEdit方法组合,修改数据表中的行。

因为,它要求所编写的代码结构更好,更容易阅读和维护,而且,出现异常时,可以取消所有的修改

2.6.3     处理DataRow的空值

实际上,判断行的列是否包含空值,很简单。

DataRow对象有IsNull方法,可以查看列是否包含空值。

DataRow.IsNull方法与Item属性使用规则类似,接受参数可以是:列名、列索引值、DataRow对象。

        Dim r As Integer

        Dim count As Integer = DataGridView1.Rows.Count

        Randomize()

        r = CInt(count * Rnd())

        Dim row As DataRow

        row = DataSet1.Tables("客户简表").Rows(r)

        row("电话") = DBNull.Value

        row("电话") = Nothing这两行都可以设置出空值来,但是不要用空字符串“”

        MsgBox("设置了第" & r & "行的电话为空值")

        row = DataSet1.Tables("客户简表").Rows(r)

        If row.IsNull("电话") Then

            MsgBox("果然," & r & ",电话是空值!")

        Else

            MsgBox("" & r & ",电话不是空值啊!")

        End If

2.6.4     删除DataRow

删除行,只需要调用DataRow.Delete方法,即可。

然而,删除行不是把它从DataTable中删除掉,而是将行,标记为,挂起删除。

为什么呢?

记住,ADONET对象模型中的数据存储对象,是作为数据缓存来执行的。

检索数据库中的数据,修改脱机模式中的数据,然后提交挂起更改——这才是处理的流程。

如果想完全从DataTable中清除行,需要提交更改。

2.6.5     清除DataRow

要从DataTable中清除行,而不是标记为挂起更改,可以用DataRowCollection对象的Remove或者RemoveAt方法。

其中,Remove的参数需要,目标行的引用

RemoveAt的参数,需要目标行的索引编号。

代码

Dim row as DataRow

Row =ds.Tables(“Customers”).Rows.Find(“ALFKI”) ‘此处要注意,Find方法只能在主键中查找

Ds.Tables(“Customers”).Rows.Remove(row)

DataSetDataTable两个对象都包含Clear方法,此方法用来清除所有的DataRow对象,而保留其结构。

2.6.6     使用DataRow.RowState属性

提交更改,到数据库,可以使用:

一、直接执行Sql语句的办法

二、使用存储过程

但是,修改行的逻辑与插入或者删除行的逻辑是不一样的,ADONET提交更改需要知道DataRow所作修改的类型,此信息被存储在DataRow对象的RowState属性中,该属性使用DataRowState类型枚举值来记录,行修改的类型。如下

常数

描述

Unchanged

2

该行不包含任何挂起更改

Detached

1

该行不是DataTable的成员

Added

4

该行已被添加到DataTable中,但还不存在于数据库

Modified

16

该行包含挂起更改

Deleted

8

该行包含挂起删除

通常,RowState属性返回的都是枚举类型的一个值。而不是,你想当然的值组合。

下面是一些方案和结果

示例

DataRowState

新创建,但却分离的行:

row=tbl.NewRow

row(“ColX”)=”InitVal”

Detached

将新行添加到数据表

Tbl.Rows.Add(row)

Added

新获取的行:

Row=tbl.Rows(0)或者row=tbl.Rows(“Colx”)

Unchanged

编辑以后:

Row.BeginEdit()

Row(“Colx”)=”NewVal”

Row.EndEdit()

Modified

删除行之后:

Row.Delete()

Deleted

2.6.7     检查DataRow中的挂起更改

使用RowState属性可以,浏览DataTable的内容并,找到修改的行。

使用DataRowItem属性,可以查看列的内容。

Item属性的第二个可以接受DataRowVersion类型的枚举值,含义如下

常量

描述

Current

512

列当前值

Original

256

-原始值

Proposed

1024

列的建议值(注意:只有在使用BeginEdit方法编辑行时才有效)

Default

1536

默认操作

一般来说,DataRow对象有行的当前值和原始值——这两个版本。

更新行之后,可以查看列的当前值和列的原始值。

代码演示:

Dim row As DataRow

row = ds.Tables("客户").Rows.Find("ALFKI")

row("公司名称") = "锐思控股"

ListBox1.Items.Add("列的当前值为- " & row("公司名称", DataRowVersion.Current))

ListBox1.Items.Add("列的原始值为- " & row("公司名称", DataRowVersion.Original))

 

使用BeginEditEndEdit时,你可以取回DataRowVersion的建议值——Proposed

 

DataRow的各种状态和使用不同的DataRowVersion取回的Item值,列表如下:

示例

当前

原始

建议

默认

新建但独立的行:

Row=tbl.NewRow

Row(“Col1”)=”Val1”

Val1

[Exception]

[Exception]

NewValue

将新行加入表:

Tbl.Rows.Add(row)

Val1

[Exception]

[Exception]

NewValue

取回的新行:

Row=tbl.Rows(0)

Retrieved_Value

寻回的值

Retrieved Value

[Exception]

Retrieved Value

第一次编辑过程中:

Row.BeginEdit()

Row(“Col1”)=”NewVal”

Retrieved_Value

寻回的值

Retrieved_Value

寻回的值

NewVal

NewVal

第一次编辑以后:

Row.EndEdit()

NewVal

Retrieved-Value

[Exception]

NewVal

第二次编辑中:

Row.BeginEdit()

Row(“Vol1”)=”NewVal2”

NewVal2

Retrieved Value

NewVal2

NewVal2

取消编辑之后:

Row.BeginEdit()

Row(“Col1”)=”valCancel”

Row.CancelEdit()

NewVal2

Retrieved value

[Exception]

NewVal2

删除行之后:

Row.Delete()

[Exception]

Retrieved Value

[Exception]

[Exception]

执行成功的操作,会影响【当前值】,但不会影响【原始值】

调用CancelEdit方法会将当前值重置为调用BeginEdit方法之前的状态,但是,不一定与原始值相同。

删除行后,如果试图查看其当前值,会产生异常,但是,仍然可以访问它的初始值。

 

Ø 关于Default

DataRowItem属性中第二个参数使用Default,不会返回列的默认值(那是数据行的DefaultValue属性的功能)

这个Default实际上表示的是,DataRow对象的Item属性中参数的默认值。

Ø 关于这里的“当前”的理解

如果不是处于“编辑行”的状态,调用Item属性,实际上省略的可选参数(第二参数)是DataRowVersion.Current;

如果处于“编辑行“的状态,调用Item属性,省略的可选参数是“建议”Proposed

 

posted @ 2008-10-15 07:32  怒杀神  阅读(790)  评论(0编辑  收藏  举报