Visual Studio 2005中的新DataSet特性(1)

本文,我将致力于在 Microsoft Visual Studio 2005 开发环境下使用这些类以及派生的类进行开发工作。对于类型化 DataSet 所作的更改和由 Visual Studio 2005 生成的新类型化 TableAdapter,是本文将要讨论的具体内容。除此之外,为开发以数据为中心的应用程序而使用的设计器和工具也是本文要讨论的内容,这些设计器和工具提供了很高的灵活性和工作效率。为了解释不同的概念和特性,我将逐步介绍开发人员在实现应用程序数据处理部分时通常要经历的过程。代码示例使用 Northwind 数据库,该数据库是 Microsoft SQL Server(以及 MSDE)7.0 版本和 Microsoft SQL Server 2000 自带的示例数据库。

  Visual Studio 2005 引入了项目数据源的概念。数据源代表在应用程序中可以使用的数据。数据库不是这些数据的唯一来源,定义数据源所使用的 Data Source Configuration Wizard 允许您从三种不同的源获取数据:

1.数据库 — 既可以是基于服务器的数据库(例如 SQL Server 或 Oracle),也可以是基于文件的数据库(例如 Access 或 SQL Server Express)。Visual Studio 自动生成类型化 DataSet 及其他类,并将它们添加到项目。

2.对象 — 任何具有公共属性的对象都可以是数据源。不一定非要实现某个特殊接口。

3.Web 服务 — 从 Web 服务创建的数据源,将创建与 Web 服务返回的数据类型相对应的对象。

  数据源具有事半功倍的作用。一方面,它可以使指定、设计和生成代表应用程序数据的强类型类更容易。另一方面,它提供一种灵活而统一的机制,可非常快速地构建功能丰富、运行高效的 WinForm 和 WebForms 用户界面。在阅读本文的过程中,您将了解到这是多么得快速、简单和灵活。

  数据库 (DataSet) 数据源的创建及其在 WinForms 应用程序中的使用是本文的重点内容。但是,以下两点也不容忽视:

一旦创建了数据源,就将以相同的方式使用每个数据源,而无需考虑其数据来自何处。也就是说,就像您可以轻松地将基于 Database 的数据源(以图形方式)绑定到网格控件或一组控件上一样,真正来自 Web 服务或您自定义业务对象的数据也要绑定到控件上。

无论是在 WinForms 还是在 WebForms 应用程序中使用,数据源均以同一种方式进行定义。不同的数据提供程序也进行了抽象,以便在只使用 DataSets 和 TableAdapters 公开数据访问的情况下,只需更改连接字符串并重新生成类就可以更改实际使用的数据库。

  类型化 DataSet 和 TableAdapter

  数据库数据源就是一个强类型 DataSet 与一个或多个强类型 DataTables 和 TableAdapters 对的组合。类型化 DataSet 的概念并不陌生,在 Visual Studio 2002/2003 中就已经提出。虽然它是从 .NET Framework 的一般 DataSet 类派生的生成类,但它具有定义好的架构以及特定于该架构的属性和方法。与此同时,DataSet 中的每个表还要生成其他三个特定于该 DataSet 的派生类 — DataTable、DataRow 和 DataRowChangeEvent 类。在这些类中,每个类都有针对相关表的特定架构、属性和方法。例如,如果根据 Northwind Employees 表定义一个数据源,最终将生成以下类:

NorthwindDataSet

EmployeesDataTable

EmployeesDataRow

EmployeesRowChangeEvent

  这四个类就组成了一个类型化 DataSet。在 Visual Studio 2005 中还将生成第五个类,名为 EmployeesTableAdapter 的类型化 TableAdapter,稍后我们将讨论这个类。当然,如果您以动态方式定义查询,那么就不能生成类型化 DataSet,而是需要使用标准的 DataSet。

  为什么要为类型化DataSet 而烦恼呢?因为除了促使您预先考虑数据的架构,而非仓促决定之外,类型化 DataSet 还提供了一些实际的优点:

1.DataSets、DataTables、DataRows 和 RowChangeEvent 都特定于正在处理的架构。

2.表、列和关系是以命名的属性方式公开的,而非一般的集合元素。

3.由于 (2) 的原因,Visual Studio 代码编辑器将支持完整的智能感知和语句结束支持。

4.可以实现编译时类型检查,例如在编译时而不是等到运行时就可以捕捉到字段名拼写错误,这同样是由于 (2) 的原因。

5.代码更简洁,可读性更强。例如以下代码行:

country = dsNorthwind.Tables ("Employees").Rows (row) ("Country")

可以替换为

country = dsNorthwind.Employees (row).Country

  总而言之,类型化 DataSets 所提供的设计时和编译时支持不但可以大大地减少启动开发时间,而且可以减少调试和稳定应用程序所需的时间。

  此外,TableAdapter 是 Visual Studio 2005 新提出的概念。它意味着强类型 TableAdapter 就是标准 DataAdapter 的强类型等价类。您可以使用 TableAdapter 连接到数据库,执行对数据库的查询或存储过程,并将数据填写到相关的 DataTable。每个 DataTable-TableAdapter 对都以一个 TableAdapter 指代。

  TableAdapter 实质上就是一个标准的 DataAdapter 的外部包装程序,它提供以下一些好处:

同一个 TableAdapter 类可在多个窗体或组件上使用,因此任何对查询/命令进行的更改都将自动在所有实例中反映出来。这不同于现有情形,在现有情形下,每个访问数据库的组件都必须拥有各自单独配置的 DataAdapter。但是如果要确保 DataTables 和 DataAdapters 保持同步,这样做将更容易。

如果要为某个指定的 DataTable 定义多个查询/命令,使用一个 TableAdapter 即可轻松完成,而不必使用多个 DataAdapters 或手动切换代码。

Fill 命令的名字可读性强、较友好,而且 TableAdapter 包含的代码可以自动为这些命令方法中的所有参数填加类型和值信息。您再也不需要将其以特定于提供程序的数据类型(例如 SqlInt)进行传递了。

  举一个简单的代码片断示例会有助于解释这些特性。在 Visual Studio 2002/2003 中,即使使用了类型化 DataSet,执行一个带有两个参数的简单查询也需要不少的代码。对于下列查询

SELECT FirstName, LastName from Employees WHERE Country = @country AND City = @city

  我们必须按以下方式编写代码:

Me.SqlAdapter1.SelectCommand.Parameters ("@country").value =
Me.CountryListbox.SelectedValue.Trim()
Me.SqlAdapter1.SelectCommand.Parameters ("@city").value =
Me.CityTextbox.Text.Trim()
Me.SqlAdapter1.Fill (Me.NorthwindDataSet.Employees)

  毫无疑问,随着参数数量的增长,代码行数也将不断增加。但更重要的是,正确地记忆和输入所有参数名的机率却大大降低了。即使准确记住了参数名,也仍然需要记住参数的数据类型。这还不是最糟糕的情况,如果错误地输入了字段名或者为值赋了错误的类型,那么很可能到运行时才会察觉。

  如果使用 Visual Studio 2005 中的 TableAdapter,一旦定义了命令 FillByCountryAndCity,在任何地方使用它时只需编写一行代码,将其以参数值形式传递即可:

Me.EmployeesTableAdapter.FillByCountryAndCity ( _
   Me.NorthwindDataSet.Employees, Me.CountryListbox.SelectedValue.Trim(),
      _    Me.CityTextbox.Text.Trim() )

  不容忽视的是,我们不仅可以从一个 TableAdapter 那里取得多个命名命令,而且这些命令都是强类型的。这就意味着在 Visual Studio 中编写代码时,可以将这些命令视为 TableAdapter 的方法,因为我们获得了完整的智能感知。我们还要对这些命令的参数进行编译时类型检查,同时,方法和参数类型定义的工具提示也可以助我们一臂之力。 TableAdapter 可以包含多个执行不同命令、接收不同参数的方法。现在我们要构建示例窗体,稍后再详细介绍 TableAdapter。

  开始行动 — 创建数据源

  本文,我们将构建一个显示 Northwind 数据库中每个定单定购信息的窗体。打开一个新的 Visual Basic WinForms 项目后,首先必须做的事情是向项目中添加一个新数据源(在创建这一示例的过程中,我们将使用 Visual Basic,但所有的操作对于 C# 也适用)。

  添加一个数据源:

1.在 Visual Studio 主菜单中,选择 Data 菜单项中的 Show Data Sources,即可显示数据源窗口(在该窗口未显示出来的情况下)。

2.在数据源窗口上,单击 Add New Data Source 工具栏按钮。该操作将启动 Data Source Configuration Wizard,该向导将 DataAdapter Configuration Wizard 的大部分功能和 Visual Studio 2002/2003 中的 DataSet 生成工具结合在一起。

3.如果您的 Visual Studio 版本仍然包含向导的欢迎页面,请选择 Next。然后将显示 Choose a Data Source Type 页面。

4.选择 Database。

5.选择 Next,显示 Choose Your Data Connection 页面。

6.选择 New Connection,显示 Add Connection 对话窗口。

7.输入要连接到 SQL Server(或 MSDE)实例以及 Northwind 数据库所需的信息。

8.选择 OK,关闭对话窗口。

9.注意,现在的连接字符串是保存为一个设置属性,可以通过下列语句访问

My.Settings.NorthwindConnectionString

在 C# 中,该语句为以下形式

VSDataSets.Properties.Settings.Default.NorthwindConnectionString;

10.选择 Next,显示 Choose Your Database Objects 页面。

11.注意,可以选择 TablesViews、Stored、Procedures 或 Functions。

展开 Tables 节点,然后选择 Orders 和 Order Details 表。我们将使用表中的所有列,但您也可以只选择应用程序所需的那些列。

12.选择 Finish,退出向导。

图 1 显示 Order Details 表展开后的数据源窗口,图中显示了该表的所有列。



图 1. 数据源窗口中的 Order Details 表

   如果要作出一些改动,重新输入 Data Source Configuration Wizard,可以选择数据源窗口工具栏的 Configure DataSet with Wizard,或者右键单击该窗口中任何元素,从得到的上下文菜单中选择向导。然而,编辑生成的 DataSet 和(多个)TableAdapter 更好的工具是 DataSet Designer。

  DataSet 设计器

  Visual Studio 2005 包含的 DataSetDesigner 是一个专门为指定和编辑 DataSets 及其相关 TableAdapters 而设计的工具。与 Visual Studio 2002/2003 中的情形比较,它有了明显的改进,因为以前我们必须使用 XML 架构编辑器来定义强类型 DataSets。在这里,DataSet 和 TableAdapter 的定义仍然保存在 .XSD 文件中,如果一定要编辑 XML 架构,也仍然是使用 XML 编辑器。不过,相似之处仅此而已。而且这样做只是为了文件格式方便起见,并不是为了使 DataSetDesigner 支持任意的 XSD 文件。

  选择数据源窗口工具栏中的 Edit DataSet with Designer,或者右键单击该窗口中任何元素,从得到的上下文菜单中选择使用设计器进行编辑,将进入 DataSet 设计器。在设计 DataSets 及其 DataTables 的过程中,设计者很可能会觉得该工具与过去设计数据库使用的其他工具十分相似。要为连接的数据库添加表,可以将数据库对象(例如表、视图、存储过程)从 Server Explorer 中拖放到设计器表面,或者在设计器上下文菜单的 Data 主菜单选项中选择 Add TableAdapter,启动 TableAdapter Configuration Wizard。因为 DataSet 还可以包含不与数据库连接而直接加载的表,所以还可以选择菜单中的 Add DataTable 来添加一个单独的表。在编辑器中同样可以添加和/或重命名列。另一个干净利落的特性是,编辑器自动识别数据库中各表之间的关系,在 DataSet 中为您定义各表之间对应的 Relations。

  图 2 显示在 DataSet 设计器中,由 Orders 和 Order Details 表组成的数据源。注意,与每个 DataTable 密切相关的是其各自对应的 TableAdapter,用于为表填写数据,或者(可选)用改动后的数据更新数据库。



图 2. 数据源中的 Orders 和 Order Details 表

 TableAdapter Configuration Wizard

  选择 Data 主菜单中的 Add Query 或 Configure(一个现有查询),或者在设计器中右键单击 TableAdapter,从得到的上下文菜单中选择这两项,可以启动 DataSet 设计器中的 TableAdapter Configuration Wizard。此向导等同于 Visual Studio 2002/2003 的 DataAdapter Configuration Wizard,只不过多了两个附加页面。一个是图 3 所示的 Choose a Query Type 页面。由于 TableAdapter 汇集了针对给定表的所有命令,因此该页面允许您定义多个 Select/Fill 命令和查询 — 任何类型的查询都可以,例如 Update、Insert、Delete 或返回单个值类型的查询。切记,这些更新查询是 TableAdapter 的命名方法,必须直接调用。而且它们还是执行 TableAdapter 的 Update 方法时自动调用的更新查询,这与执行 DataAdapter.Update 方法一样。

  向导中另一个增加的页面是 Choose Methods to Generate 页。在该页中可以选择您所定义的每个查询/命令方法名(一个或多个)。向导为每个命令都提供一个 Fill 和 Get 方法,如图 3 所示。Fill 要求提供需要填写数据的 DataTable,而 Get 方法将返回填写后新创建的 DataTable。



图 3. Choose Methods to Generate 页面中的 Fill 和 Get 方法

  按照典型的用法,您可以为一个 TableAdapter 定义多个 Fill 命令,它将返回同一种架构(多个列),只是 WHERE 子句不同而已。这也是默认情况下向导会为方法名提供一个 FillBy 和 GetDataBy 前缀的原因。不过,您当然可以选择任何名字作为方法名。

  虽然 TableAdapter 可以包含多个 Fill 命令,但在调用其中的 Update 方法时却只能执行一组更新命令。这组命令根据 TableAdapter 的主查询自动生成。第一次创建 TableAdapter 时定义的查询被认为是它的主查询。如果随后定义的任何查询返回的架构与主查询的架构不同,则设计器将显示一个消息框进行提醒。另一方面,如果您修改了主查询的架构,Visual Studio 将修改其他查询的架构以与之匹配。

  通过 TableAdapter Configuration Wizard 添加新命令

  现在,我们将另一个命令添加到 Orders 表的 TableAdapter。

1.选择数据源窗口工具栏中的 Edit DataSet with Designer,打开 DataSet 设计器。

2.选择 Orders 表的 TableAdapter,再从该表的上下文菜单中选择 Add Query。

3.如果您的 Visual Studio 版本中仍然有 Welcome 页,请选择页面上的 Next。之后,显示 Choose a Command Type 页面。

4.要接受默认的 SQL 语句,选择 Next。这时显示 Choose a Query Type 页面。

5.要接受默认的 SELECT 语句,选择 Next。然后,显示 Specify a SQL SELECT Statement 页面。

6.输入下列 SQL 语句。

SELECT OrderID, CustomerID, EmployeeID, OrderDate, RequiredDate, 
ShippedDate, ShipVia, Freight, ShipName, ShipAddress, ShipCity, 
ShipRegion, ShipPostalCode, ShipCountry 
FROM Orders
WHERE CustomerID = @CustID

此后,返回由进行查询的 @CustID 参数指定的所有客户 Orders。

7.选择 Next,显示 Choose Methods to Generate 页。

8.选中页面上的两个复选框。将方法名分别改为 FillByCustomer 和 GetDataByCustomer。

9.选择 Next,然后选择 Finish,完成该过程。

  现在请您看一下 DataSet 设计器中的 OrdersTableAdapter,这时出现了第二个命令对 — FillByCustomer 和 GetDataByCustomer,CustomerID 的值就是该命令的一个参数。那么,该方法的参数属于哪种 .NET 类型呢?让我们来讨论一下。

  后台简介

  在 DataSet 设计器或任何相关向导中进行修改时,Visual Studio 都会为一组类型化的类生成代码。具体而言,它将生成以下类:

1.DataSet 类

2.DataTable 类

3.TableAdapter 类

4.DataRow 类

5.DataRowChangeEvent 类

  除了 DataSet 类(每个数据源只有一个)之外,其余四个类对于 DataSet 中定义的表而言是相同的。可通过以下操作查看这些类的代码:

1.在 Solution Explorer 窗口中,单击工具栏上的 Show All Files 按钮,显示与项目相关的所有文件。

2.展开 NorthwindDataSet.xsd 文件的节点。

3.双击文件节点上的 NorthwindDataSet.Designer.vb。这就是为实现组成 DataSet 的那些类而生成的代码。

4.打开代码窗口左上角的 Listbox,将看到本文所用类的列表:

NorthwindDataSet

Order_DetailsDataTable

OrdersDataTable

Order_DetailsRow

OrdersRow

Order_DetailsRowChangeEvent

OrdersRowChangeEvent

Order_DetailsTableAdapter

OrdersTableAdapter

5.选择左侧 Listbox 中的 OrdersTableAdapter 类。

6.再选择右侧 Listbox 中的 FillByCustomer 方法。此时将显示该方法的代码,如下所示:

      Public Overloads Overridable Function FillByCustomer(ByVal dataTable 
         As NorthwindDataSet.OrdersDataTable, ByVal CustID As String) As Integer
            Me.Adapter.SelectCommand = Me.CommandCollection(1)
            If (CustID Is Nothing) Then
                Throw New System.ArgumentNullException("CustID")
            Else
                Me.Adapter.SelectCommand.Parameters(0).Value = CType(CustID,String)
            End If
            If (Me.m_clearBeforeFill = true) Then
                dataTable.Clear
            End If
            Dim returnValue As Integer = Me.Adapter.Fill(dataTable)
            Return returnValue
        End Function

  从该代码片断中我们可以了解几件事情。

FillByCustomer 方法实际上带有两个参数 — 一个是要填写的 OrdersDataTable,另一个是字符串类型的 CustID 参数。

不用检查完文件中的所有代码,我们就可以发现 TableAdapter 类维护着一个命令集合,正是从这个集合中自动将正确的命令指派给 .NET DataAdapter,供其使用这些命令自动与数据库通讯。

如果状态参数的 AllowDBNull 属性设置为 False,该方法会不断检查是否有状态参数的实例传递进来。

如果 DataAdapter 的 SelectCommand 属性已经配置好,则 CustID 的参数值将赋给该属性的参数。

所有配置设置完毕后,即可调用 DataAdapter.Fill () 方法来填写 OrdersDataTable。

  切记,这些代码都不需要您动手编写。它们是已经生成并配置好的代码。花些时间查找一下为您生成的其他类。您将更深入、更广泛地理解这些类的实现方式,甚至还可能学到几个有用的编程技巧。

  注 尽管类型化 DataSet 及其相关类(包括 TableAdapters)均在一个源文件中生成,但 TableAdapters 是在某个独立的命名空间中生成的。这说明应该将实体对象 (DataSets) 与真正的数据访问对象 (TableAdapters) 区分开来。

你可能感兴趣的:(.net)