首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

21.5.2  数据列

DataColumn对象定义了数据表中某列的属性,例如该列的数据类型,该列是否为只读,以及其他属性。列可以在代码中创建,或者由运行库自动生成。

在创建一个列时,给它指定名称是很有用的,否则运行库就会为该列生成一个名称,其格式是Columnn其中n是一个递增的数字。

列的数据类型可以在构造函数中提供,也可以通过设置DataType属性来指定。把数据加载到数据表中后,就不能改变列的数据类型了,否则会抛出ArgumentException异常。

创建的数据列可以包含表21-3所示的.NET Framework数据类型。

  21-3

Boolean

Decimal

Int64

TimeSpan

Byte

Double

Sbyte

UInt16

Char

Int16

Single

UInt32

DateTime

Int32

String

UInt64

一旦创建好,就要给DataColumn对象设置其他属性,例如该列是否可为空或者设置默认值。下面的代码段显示了给DataColumn设置的一些常见选项:

DataColumn customerID = new DataColumn("CustomerID" , typeof(int));

customerID.AllowDBNull = false;

customerID.ReadOnly = false;

customerID.AutoIncrement = true;

customerID.AutoIncrementSeed = 1000;

DataColumn name = new DataColumn("Name" , typeof(string));

name.AllowDBNull = false;

name.Unique = true;

可以给DataColumn设置如表21-4所示的属性。

  21-4

   

   

AllowDBNull

如果为true,该列就可以设置为DBNull

AutoIncrement

定义自动生成的列值为一个递增的数字

AutoIncrementSeed

定义AutoIncrement列最初的种子值

AutoIncrementStep

用默认的步骤定义自动生成列值的步骤

Caption

可以用于在屏幕上显示列名

ColumnMapping

指定当DataSet通过调用DataSet.WriteXml来保存时,列如何映射到XML

ColumnName

列名。如果没有在构造函数中设置,就由运行库自动生成

DataType

列的System.Type

DefaultValue

可以定义列的默认值

Expression

该属性定义表达式用于所计算的列

1. 数据行

这个类构成了DataTable类的另一部分。数据表中的列根据DataTable类来定义表中的实际数据用DataRow对象来访问。下面的示例说明了如何访问数据表中的行。首先是连接:

string source = "server=(local)\\NetSDK;" +

             "uid=QSUser;pwd=QSPassword;" +

             "database=northwind";

string select = "SELECT ContactName,CompanyName FROM Customers";

SqlConnection  conn = new SqlConnection(source);

下面的代码显示了SqlDataAdapter类,它用于选择DataSet中的数据。SqlDataAdapter使用SQL子句,在DataSet中用下面查询的结果填写表Customers。将在21.7节中进一步讨论SqlDataAdapter类。

SqlDataAdapter da = new SqlDataAdapter(select, conn);

DataSet ds = new DataSet();

da.Fill(ds , "Customers");

在下面的代码中注意使用DataRow的索引器访问数据行上的值。给定列的值可以用几个重载的索引器来检索,这样就可以通过已知的列号、列名或DataColum来检索数据的值:

foreach(DataRow row in ds.Tables["Customers"].Rows)

   Console.WriteLine("'{0}' from {1}" , row[0] ,row[1]);

DataRow最吸引人的一个方面就是它的版本功能。DataRow可以接收某一行上指定列的各个值,其版本见表21-5

  21-5

DataRowVersion

   

Current

列中目前存在的值,如果没有进行编辑,该值与初值相同。如果进行了编辑,该值就是最后输入的一个有效值

Default

默认值(列的任何默认设置)

Original

最初从数据库中选择出来的列值。如果调用了DataRowAcceptChanges方法,该值就更新为当前值

Proposed

对列进行逐步的修改时,可以检索到这个已改变的值。如果在行上调用了方法BeginEdit(),并进行了修改,每一列都会有一个推荐值,直到调用了EndEdit()CancelEdit()为止

 

可以以许多方式使用给定列的版本。例如,在数据库中更新数据行时,常常使用如下SQL语句:

UPDATE Products

SET     Name = Column.Current

WHERE  ProductID = xxx

AND     Name = Column.Original;

显然,这段代码永远不会编译,但它说明了列的初值和当前值的一个用法。

要从DataRow索引器中检索某个版本的值,应使用索引器方法,把DataRowVersion值作为一个参数。下面的代码段说明了如何获得DataTable中每一列的所有值:

foreach (DataRow row in ds.Tables["Customers"].Rows )

{

  foreach ( DataColumn dc in ds.Tables["Customers"].Columns )

  {

    Console.WriteLine ("{0} Current  = {1}" , dc.ColumnName ,

                                        row[dc,DataRowVersion.Current]);

    Console.WriteLine ("    Default  = {0}" , row[dc,DataRowVersion.Default]);

    Console.WriteLine ("    Original = {0}" , row[dc,DataRowVersion.Original]);

  }

}

整个数据行有一个状态标志RowState,可以用于确定在返回数据库时需要对该行进行什么操作。RowState标志跟踪对DataTable所作的所有改变,例如添加新行、删除现有的行,改变表中的列。当数据与数据库保持一致时,行的状态标志用于确定应执行什么SQL操作。这些标志由DataRowState枚举定义,如表21-6所示。

  21-6

DataRowState

   

Added

把新数据行添加到DataTableRows集合中。在客户机中创建的所有行都设置为这个值,最终在与数据库保持一致时,会使用SQL INSERT语句

Deleted

通过DataRow.Delete()方法把DataTable中的数据行标记为删除。该行仍存在DataTable中,但在屏幕上看不到它(除非显式设置DataView)DataView在下一章讨论。在DataTable 中标记为已删除的Rows将在与数据库保持一致时从数据库中删除

Detached

数据行在创建后立即显示为这个状态,调用DataRow.Remove()也可以返回这个状态。分立的行不是任何DataTable的一部分,因此处于这种状态的行不能使用任何SQL语句

Modified

如果列中的值发生了改变,就会修改一行数据

Unchanged

自从最后一次调用AcceptChanges以来,数据行都没有发生改变

 

行的状态也取决于在其上调用的方法。一般在成功更新数据源(即把改变返回数据库后)之后调用AcceptChanges方法。

修改DataRow中数据最常见的方式是使用索引器,但如果对数据进行了许多修改,就需要考虑使用BeginEdit() EndEdit()方法。

在对DataRow中的行进行了修改后,就会在该行的DataTable上引发ColumnChanging事件。可以重写DataColumnChangeEventArgs 类的ProposedValue属性,按照需要修改它。这是在列值上进行某些数据有效性验证的一种方式。如果在进行修改前调用BeginEdit()方法,就不会引发ColumnChanging事件,可以进行多次修改,再调用EndEdit()方法,保存这些修改。如果要回退到初值,应调用CancelEdit()

DataRow可以以某种方式链接到其他数据行上,在数据行之间能够建立可导航的链接,这在主/从数据表中非常常见。DataRow包含一个GetChildRows()方法,该方法可以从同一个DataSet的另一个表中把一组相关行返回为当前行。这些将在本章后面的“数据关系”一节中介绍。

2. 模式的生成

DataTable创建模式有3种方式:

       让运行库来完成

       编写代码来创建表

       使用XML模式生成器

(1) 运行生成的模式

前面的DataRow示例用下面的代码从数据库中选择数据,并生成一个DataSet

SqlDataAdapter da = new SqlDataAdapter(select , conn);

DataSet ds = new DataSet();

da.Fill(ds , "Customers");

这是很容易使用的,但也有几个缺点。例如,必须利用默认的列名来处理—— 这是可以的,但在某些情况下,还要把物理数据库的列(PKID)重新命名为一个用户友好性更高的名称。

自然,可以在SQL子句中重新给列指定别名,例如SELECT PID AS PersonID FROM PersonTable。最好不要在SQL中重新给列命名,因为列实际上只需要在屏幕上显示一个“比较好”的名称即可。

自动生成DataTable/DataColumn的另一个潜在的问题是不能控制运行库为列选择的数据类型。运行库可以确定正确的数据类型,但有时需要对此有更多的控制。例如,为给定的列定义枚举类型,以简化类的用户代码。如果接受运行库生成的默认列类型,该列就可能是一个32位的整数,而不是有预定选项的枚举。

最后,也是最有可能出的问题是,在使用自动生成的表时,不能对DataTable中的数据进行类型安全的访问—— 索引器就会返回object的实例,而不是派生的数据类型。如果要用代码对表达式进行类型转换,就可以跳过下面的章节。

(2) 手工编码的模式

用生成代码来创建DataTable再用相关的DataColumns来填充是相当简单的。本节的示例将访问Northwind数据库中的Product如图21-6所示。

  21-6

下面的代码生成了一个DataTable,对应于上面的模式(但没有包含可为空的列)

public static void ManufactureProductDataTable(DataSet ds)

{

   DataTable   products = new DataTable("Products");

   products.Columns.Add(new DataColumn("ProductID", typeof(int)));

   products.Columns.Add(new DataColumn("ProductName", typeof(string)));

   products.Columns.Add(new DataColumn("SupplierID", typeof(int)));

   products.Columns.Add(new DataColumn("CategoryID", typeof(int)));

   products.Columns.Add(new DataColumn("QuantityPerUnit", typeof(string)));

   products.Columns.Add(new DataColumn("UnitPrice", typeof(decimal)));

   products.Columns.Add(new DataColumn("UnitsInStock", typeof(short)));

   products.Columns.Add(new DataColumn("UnitsOnOrder", typeof(short)));

   products.Columns.Add(new DataColumn("ReorderLevel", typeof(short)));

   products.Columns.Add(new DataColumn("Discontinued", typeof(bool)));

   ds.Tables.Add(products);

}

可以改变DataRow示例中的代码,使用新生成的表定义:

string source = "server=(local)\\NetSDK;" +

                "integrated security=sspi;" +

                "database=Northwind";

string select = "SELECT * FROM Products";

SqlConnection conn = new SqlConnection(source);

SqlDataAdapter cmd = new SqlDataAdapter(select, conn);

DataSet ds = new DataSet();

ManufactureProductDataTable(ds);

cmd.Fill(ds, "Products");

foreach(DataRow row in ds.Tables["Products"].Rows)

   Console.WriteLine("'{0}' from {1}", row[0], row[1]);

ManufactureProductDataTable方法创建一个新DataTable,依次添加每个列,最后把这个表添加到DataSet中表的清单上。DataSet有一个索引器,它的参数是表名,给调用者返回该DataTable

上面的示例仍不是类型安全的,因为在列上使用了索引器来检索数据。最好是有一个类(或一组类)派生于DataSet DataTable DataRow,为表、行和列定义类型安全的存取器。可以自己生成这段代码,这并不是特别乏味,最终将得到可以进行类型安全访问的类。

如果不愿意自己生成这些类型安全的类,可以使用帮助。.NET Framework允许使用XML模式来定义DataSetDataTables和本节介绍的其他类。本章后面的XML模式一节详细介绍了这个方法。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论