• 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

     

  • 相关阅读:
    五种常见的 PHP 设计模式(收藏)
    写年度工作总结
    关于window.open和window.showdialog返回值的问题
    50个令人叹为观止的JavaScript应用站点[转]
    10大免费FLV播放器下载[转]
    6个去掉图片上的文字的技巧实用简单
    mysql命令大全(转)
    10款替代Windows Media Player的播放器
    Editplus FTP远程访问Ubuntu
    C++ 元编程 Meta Programming
  • 原文地址:https://www.cnblogs.com/lizunicon/p/1311334.html
Copyright © 2020-2023  润新知