分享

数据访问: 在 Visual Basic .NET 中使用存储过程1

 ycgclf 2011-01-06

在 Visual Basic .NET 中使用存储过程

发布日期: 4/1/2004 | 更新日期: 7/6/2004

Billy Hollis

摘要:Billy Hollis 讲述了存储过程对于复杂系统的益处,将存储过程提到了超出演示软件的高度,并提供了一些手头的示例,以说明如何访问存储过程以及如何开始在应用程序中使用这些存储过程。

下载 StoredProcVB.NET.exe

我们这些作者倾向于将软件分为两类:实际软件演示软件。 实际软件就是那些在现实中能够真正运作的软件。 演示软件则是为了说明某些编程概念而编写的软件。

各种文章和书籍上所见到的大多数代码都是演示软件。 这些软件必须比实际软件来得简单;否则,读者将陷入与所说明概念毫无关系的各种细节中。 但有时演示软件简单得过了火。 一味追求简单就可能忽略在编写实际软件时所必需的一些细节。

我最近注意到的这种例子是关于数据访问的。 我所看到的每个数据访问示例基本上都使用 SQL 语句对关系数据库进行读写访问,这样的关系数据库如 Microsoft SQL Server?。 然而,在现实中,这种做法被认为是一种错误的编程习惯,可能一些小型的、有限的系统除外。 一个恰当构造的 n 层应用程序使用存储过程进行数据访问,而不是使用 SQL 语句。

在概念上,存储过程类似于程序中的函数。 存储过程获得输入参数,以黑盒子 形式运行,并返回适当的信息。 与函数的不同之处在于,存储过程由数据库引擎执行,而不是在程序内部执行的。 这意味着必须利用一种能够与数据库接口的技术从存储过程获取信息或向其中输入信息。 在 Microsoft Visual Basic? 6.0 中,这个技术就是传统的 ADO。 在 Visual Basic .NET 中,我们则可以通过 ADO.NET 完成。

如同对很多编程任务一样,Visual Basic .NET 使得利用存储过程访问数据的操作比 Visual Basic 6.0 利用存储过程进行数据访问容易得多。 Visual Basic .NET 提供了一些向导以帮助完成此过程,只要您知道如何避免一些太随意的语句,即便是利用 ADO.NET 从头开始编写这种逻辑也不是过分复杂的事情。

本文包括一些在利用 ADO.NET 对存储过程进行操作时的基本方法,首先介绍了一种只读操作,然后继续讲述使用存储过程进行插入、删除和更新数据。

即便您不能熟练地编写存储过程,您也将从本文中获益匪浅。 大型编程团队中的很多开发人员都需要能够使用由别人编写的存储过程。 我们的示例之一就是需要将一个存储过程插入到示例数据库中,但我们将循序渐进地讲述该项任务。

简要回顾 ADO.NET

对本文而言,我必须假设您已经具备了有关 ADO.NET 的基础知识。如果您尚未使用 ADO.NET 中的 DataAdaptersDataSetsCommand 对象完成一些任务,那么您应该读读有关 ADO.NET 的入门文章,包括 Rocky 为本专栏撰写的一篇文章,名为 ADO.NET and You

简要回顾一下,DataSets 在 ADO.NET 中用作数据容器,在从数据库断开时使用。 DataSet 包含一个或多个 DataTables,其中每个包含一个行集。 对于熟悉传统的 ADO 的开发人员而言,DataTable大致可看作是一个断开的记录集。

DataAdapters 在连接到数据库时起作用。 一个 DataAdapter 的任务或者是利用来自数据库的数据填充其中一个 DataTables,或者是将 DataTable 中的更改写回数据库中,或者是这二者都进行。

DataAdapters 需要 Command 对象来执行各种数据库操作。 Command 对象中或者包含一条 SQL 语句,或者包含一个存储过程名称,用以指定要如何完成数据访问。 每个 DataAdapter都有四个属性,表明用于四种数据访问类型中每一种类型的命令对象:

SelectCommandCommand 对象用于从数据库中选择数据。

UpdateCommandCommand 对象用于更新数据库中现有的记录。

InsertCommand: Command 对象用于向数据库中插入新记录。

DeleteCommandCommand 对象用于删除数据库中现有的记录。

我们可以用图的形式来表示这些对象及其关系,如图 1 所示。

bdadotnetarch08_03

1. 用于访问存储过程的主要 ADO.NET 类,及其相互关系

到目前为止,您所看到的演示软件示例可能都是将其 Command 对象配置为利用 SQL 语句进行数据访问。 实际上,其中的某些示例很可能还完全绕过了 Command 对象的创建,因为 DataAdapter的其中一个构造函数允许在后台创建用于选择数据的 Command 对象。 在开始使用存储过程之前,让我们演示这样一个示例以作为比较。

本文中所有示例都使用 SQL Server 所附带的 Northwind 示例数据库。 我们还将使用专门针对 SQL Server 而创建的 ADO.NET 类,而不是使用通用的 OLE DB 类。 为了便于访问这些 SQL Server 类,所有示例都需要在应用程序的代码最上面包括以下一行:

 Imports System.Data.SQLClient

现在,开始我们的第一个示例,即不通过存储过程执行数据访问。 在此示例中,我们将检索 Northwind 数据库 Products 表中所有的产品。 创建一个新的 Windows 应用程序,并在所显示的空白 Form1 上放置一个按钮和一个 DataGrid。 将 DataGridAnchor 属性设置到全部四条边,这样一来,它就会随着窗体的扩展而扩展。 在按钮的 Click 事件中,加入以下代码:

 Dim sConnectionString As String = _
"server=localhost;uid=sa;pwd=;database=Northwind"
Dim sSQL As String = "SELECT * FROM Products"
Dim daGetProducts As New SqlDataAdapter(sSQL, sConnectionString)
Dim dsProducts As New DataSet()
daGetProducts.Fill(dsProducts, "Products")
DataGrid1.DataSource = dsProducts.Tables("Products")

根据计算机的具体配置,您可能需要更改连接字符串。 然而,只要建立了数据库连接,代码的其他部分就应该运行正常。 此演示软件显示了用于填充和使用 DataSet 的最简单且可行的方法。

请注意,该代码并没有创建 Connection 对象或 Command 对象。 在现实中,如果没有这些对象,ADO.NET 是不能运行的,但这些对象是在后台创建并使用的。 用于实例化 SqlDataAdapter 的代码行传递进来一个 SQL 字符串(以配置这个后台 Command 对象)和一个连接字符串(以配置这个后台 Connection 对象)。

我们可以更改此代码以使用显式的 ConnectionCommand 对象,这样离演示软件稍微远了一些。 在窗体上放置另一个按钮,并将以下代码加入该按钮的 Click 事件中:

 Dim sConnectionString As String = _
"server=localhost;uid=sa;pwd=;database=Northwind"
Dim sSQL As String = "SELECT * FROM Products"
Dim cnNorthwind As New SqlConnection(sConnectionString)
Dim cmdProducts As New SqlCommand(sSQL, cnNorthwind)
Dim daGetProducts As New SqlDataAdapter(cmdProducts)
Dim dsProducts As New DataSet()
daGetProducts.Fill(dsProducts, "Products")
DataGrid1.DataSource = dsProducts.Tables("Products")

此代码显示了 DataAdapters 的更常见的使用方法;即通过显式创建 ConnectionCommand 对象并将这些对象附加到 DataAdapter。 通过在实例化 DataAdapter 时传递到 cmdProducts 中,DataAdapterSelectCommand 得以自动设置。 随后,DataAdapter 马上就可用于访问数据库。

此代码的结果与前一示例的结果完全相同。 但这段代码较接近于真实软件,它的更多方面还是像演示软件,因为数据访问是通过 SQL 语句完成的。

利用简单的存储过程获取数据

如何更改此演示软件以使用存储过程呢? 只需更改其中的两行。 在窗体上放置另一个按钮,并将以下代码加入该按钮的 Click 事件中:

 Dim sConnectionString As String = _
"server=localhost;uid=sa;pwd=;database=Northwind"
Dim cnNorthwind As New SqlConnection(sConnectionString)
Dim cmdProducts As New _
SqlCommand("Ten Most Expensive Products", cnNorthwind)
cmdProducts.CommandType = CommandType.StoredProcedure
Dim daGetProducts As New SqlDataAdapter(cmdProducts)
Dim dsProducts As New DataSet()
daGetProducts.Fill(dsProducts, "Products")
DataGrid1.DataSource = dsProducts.Tables("Products")

这段代码不再使用 SQL 语句,取而代之的是在实例化 Command 对象时将使用存储过程名称。 此外,该 Command对象的 CommandType 属性必须设置为 StoredProcedure

从该行以后,代码与前一示例完全相同,但返回不同的数据。 该存储过程会找出十项最贵的产品,且仅返回每项产品的名称和价格。

带输入参数的存储过程

此示例相当简单,因为其存储过程不需要任何输入参数。 也就是说,找出十项最贵产品的操作并不需要任何外部信息。 该存储过程不需要任何外部帮助即可完成操作。 然而,大多数存储过程却的确需要输入参数以执行其功能。 作为下一个示例,让我们看看如何向存储过程传递输入参数。 我们将利用 Northwind 数据库中已有的一个名为 CustOrderHist 的存储过程,并使用 CustomerID 获取相关客户的所有订单。

在我们一直使用的窗体上创建另一个按钮,并在该按钮的 Click 事件中加入以下代码:

 Dim sConnectionString As String = _
"server=localhost;uid=sa;pwd=;database=Northwind"
Dim cnNorthwind As New SqlConnection(sConnectionString)
Dim cmdOrders As New SqlCommand("CustOrderHist", cnNorthwind)
cmdOrders.CommandType = CommandType.StoredProcedure
' Set up parameter for stored procedure
Dim prmCustomerID As New SqlParameter()
prmCustomerID.ParameterName = "@CustomerID"
prmCustomerID.SqlDbType = SqlDbType.VarChar
prmCustomerID.Size = 5
prmCustomerID.Value = "ALFKI"
cmdOrders.Parameters.Add(prmCustomerID)
Dim daGetOrders As New SqlDataAdapter(cmdOrders)
Dim dsOrders As New DataSet()
daGetOrders.Fill(dsOrders, "Orders")
DataGrid1.DataSource = dsOrders.Tables("Orders")

这段代码看起来很像前一示例中的代码,不同之处在于,在创建 Command 对象之后,为该对象配置了一个 Parameter对象并将其添加到了该命令的参数集合中。 在此示例中,我们硬编码了一个客户 ID(更接近于演示软件),而且在通常情况下参数的 Value属性应该被设置成某些用户输入数据。 然而,可完全按此示例中所示的方式设定该参数的其他属性。

此示例显式地设置了所有参数。 有些开发人员喜欢这种风格,且这种风格适用于指导目的。 然而,有些开发人员更喜欢另一种等效的形式,这种形式的代码行更少:

 Dim sConnectionString As String = _
"server=localhost;uid=sa;pwd=;database=Northwind"
Dim cnNorthwind As New SqlConnection(sConnectionString)
Dim cmdOrders As New SqlCommand("CustOrderHist", cnNorthwind)
cmdOrders.CommandType = CommandType.StoredProcedure
cmdOrders.Parameters.Add(New _
SqlParameter("@CustomerID", SqlDbType.VarChar, 5))
cmdOrders.Parameters("@CustomerID").Value = "ALFKI"
Dim daGetOrders As New SqlDataAdapter(cmdOrders)
Dim dsOrders As New DataSet()
daGetOrders.Fill(dsOrders, "Orders")
DataGrid1.DataSource = dsOrders.Tables("Orders")

这段代码的功能与前一示例的功能完全相同。 然而,对每个参数,它只需两行代码,而不是六行。 如果一个存储过程具有很多参数(稍后的一些示例就会有很多参数),这样一来所需的代码行数可能会产生很大的区别,所以从此往后,我们将使用这种形式。

使用存储过程更新数据库

以上的几个示例使用了存储过程从数据库中获取信息。 在一些复杂的应用程序中,也经常使用存储过程来更新、插入和删除记录。 让我们看看如何利用 ADO.NET 进行这些操作。

作为第一个示例,我们将允许 Visual Studio? .NET 中的向导为我们编写一组存储过程,并创建相应代码以使用这些过程。 尽管我们只需为此示例编写最少量的代码,但是仔细查看向导所创建的代码能够帮助我们理解与存储过程接口的过程,以便除获取数据之外还可以进行其他操作。

对于本示例,我们将使用 Northwind 示例数据库中的 Customers 表。 在安装 Northwind 数据库时,其中并不包含用于更新、插入或删除客户的存储过程,但 Visual Studio .NET 中的 DataAdapter 配置向导可很方便地编写一些存储过程。

开始一个新的 Windows Application 项目。 在空白的 Form1 上,放置一个 DataGrid和两个按钮。 跟前面一样,将 DataGridAnchor 属性更改为定位到所有四条边。 将按钮命名为 btnFillbtnUpdate,并将其 Text 属性分别更改为 FillUpdate

转至 ToolboxData 选项卡,将一个 SqlDataAdapter 控件拖到窗体上然后释放。 这样将打开一个 DataAdapter 配置向导。 单击 Next 按钮,开始在向导中输入信息。

首先,需要选择一个到 Northwind 数据库的连接,或者,如果列表中没有可用连接,则单击 New Connection按钮,创建一个连接。 然后单击 Next 按钮。

下一个屏幕中包含用于访问数据的三种可选途径。 该屏幕如图 2 所示。

bdadotnetarch08_03

2. DataAdapter 选择数据访问类型

在此处,大多数演示软件示例会使用第一个选项以使用 SQL 语句。 然而,我们将改用第二个选项,并让向导为我们创建一些存储过程。 选择 Create new stored procedures 选项,然后单击 Next 按钮。

下一个屏幕需要一条 SQL 语句以指明初始时从数据库中获取的数据。 然而,并不会直接使用此 SQL 语句。 此 SQL 语句中的信息将用于构建进行实际数据访问的存储过程。 为了使此示例保持简单明了,请输入 SQL 语句 SELECT * FROM Customers,然后按 Next 按钮。

在此处,向导要求提供将要创建的存储过程的名称。 有四个存储过程 — 选择、更新、插入和删除操作。 按以下方式命名:

选择:MSDNSelectCustomers

更新:MSDNUpdateCustomer

插入:MSDNInsertCustomer

删除:MSDNDeleteCustomer

保持选中 Yes, create them in the database for me 选项。 此时,向导屏幕应该类似图 3 所示。

bdadotnetarch08_03

3. DataAdapter 向导将要创建的存储过程命名

单击 Next 按钮。 向导将创建这些存储过程,并在状态屏幕上显示其进度。 完成后,您可以单击 Finish按钮,退出向导。

该向导创建了一个完全配置好的 DataAdapter,但并未创建 DataSet 来容纳数据。 下一步,我们将进行该操作。 从 ToolboxData 选项卡中,拖过一个 DataSet 控件。 当显示其配置屏幕时,选择 Untyped dataset

现在,就可利用该 DataAdapter 填充该数据集了。 在 btnFillClick事件中,加入以下两行代码:

 SqlDataAdapter1.Fill(DataSet1, "Customers")
DataGrid1.DataSource = DataSet1.Tables("Customers")

btnUpdateClick 事件中,加入以下一行:

 SqlDataAdapter1.Update(DataSet1, "Customers")

现在,我们就拥有了一个可正常工作的演示软件,它使用存储过程进行数据访问。 您可以运行该程序,并单击 Fill 按钮以获取网格中的用户列表。 然后,您可以在网格中编辑用户数据,并选择 Update 按钮将这些更改返回到数据库中。

如果编辑第一列,也就是 CustomerID,将发生异常,因为您不能在 SQL Server 中更新一条数据库记录中的主键。

查看一下向导所生成的代码还是很有指导意义的,所有这些代码最初都隐藏在 Windows Form Designer generated code 区域中。 单击该区域相应的加号,展开该代码。 注意以下代码,这些代码会实例化 SQLDataAdapter及其所需的四个命令对象:

 Me.SqlDataAdapter1 = New System.Data.SqlClient.SqlDataAdapter()
Me.SqlSelectCommand1 = New System.Data.SqlClient.SqlCommand()
Me.SqlInsertCommand1 = New System.Data.SqlClient.SqlCommand()
Me.SqlUpdateCommand1 = New System.Data.SqlClient.SqlCommand()
Me.SqlDeleteCommand1 = New System.Data.SqlClient.SqlCommand()

再往下,您将看到用于配置每个命令对象并为其创建参数集合的代码。 这段代码类似于前面的使用带参数的存储过程的示例中的代码。 然而,向导所生成的代码使用了附加的一些参数属性,以允许这些参数属性与更改数据的各种存储过程一起使用。 例如,用于为 SQLInsertCommand1 创建 CompanyName 参数的代码:

 Me.SqlInsertCommand1.Parameters.Add(New _
System.Data.SqlClient.SqlParameter("@CompanyName", _
System.Data.SqlDbType.NVarChar, 40, "CompanyName"))

在前面的示例中,我们仅为参数名称、数据类型和长度设置了属性。 这段代码还将该参数的 SourceColumn 属性设置为 CompanyName 值。 该属性表明了该数据集的 CustomersDataTable 中与此参数对应的字段。 这就允许在插入操作中将 DataTable 中的值自动插入该参数的 Value 属性中。 让我们更详细地讨论一下这一点。

SQLDataAdapterUpdate 方法被调用时,它会更新 DataSet 中的单个 DataTable。 对 DataTable 逐行进行检查,查找需要更新、插入或删除的行。 当发现要将新的一行插入数据库时,SQLDataAdapter 会使用其 InsertCommand 属性所设定的 Command 对象。 在这里,该 Command 对象访问了 MSDNInsertCustomer 存储过程。

在能运行该存储过程之前,必须从正被插入的行填充每个参数的 Value 属性。 用于配置 SQLDataAdapter1的代码将该存储过程的每个参数与 DataTable 中的相应字段关联起来。 这就允许将新的 DataTable 行的数据自动传送到该存储过程的参数。

其他存储过程的参数可类似地进行配置。 有一个不同之处值得注意。 其他存储过程会传入 DataTable中数据的初始值,这些值用于检查数据在您不知情的情况下未发生更改。 也就是说,如果您获取了某些数据,而在您尝试更新之前别人已经进行了更改,您则会得到一个并发异常。 启动上述程序,获取客户信息,然后使用某个工具(如 SQL Enterprise Manager)更改记录中的某项内容,您就可以看到这种情况发生。 如果您在示例程序中更改同一条记录并试图更新,则会得到一个并发异常。

从存储过程返回值

以上示例在一个方面存在着不足。 Northwind Customers 表使用了字母数字主键,且应用程序在插入数据时必须组成这些主键。 也就是说,如果您利用以上的程序插入一条新记录,则必须自己为 CustomerID 创建一个 5 个字符的值。

在实际软件中,更常见的做法是为新记录自动生成主键。 这种主键通常是一个按顺序分配的长整数。

有两种基本方法可用于为新记录设置主键。 应用程序可以调用一个存储过程以生成下一个可用的 ID,然后该应用程序可以将该 ID 直接放入 DataSet 的新行中。 或者,用于插入记录的存储过程可以为该记录派生新的 ID,然后作为一个返回值将其传递回该应用程序中。

第一种方法需要一些额外的逻辑以获取新 ID 并将其放到新记录中的合适位置处。 使用存储过程进行插入操作与以上的示例类似。

然而,第二种方法需要一种新的参数用于存储过程。 到目前为止,我们所见到的所有参数都是默认类型,也就是一个输入参数。 实际上,有四种类型的参数:

Input

这种参数仅用于将信息从应用程序传递到存储过程。

InputOutput

这种参数可以将信息传入存储过程,也可将信息从存储过程传递回应用程序。

Output

这种参数只将信息从存储过程传递回应用程序。

ReturnValue

这种参数代表从存储过程返回的值。 这种参数并不出现在 SQL Server 中用于存储过程的参数列表中。 它只与存储过程的 RETURN 语句中的某个值相关。

当存储过程为主键生成一个新值时,常用的做法是利用该存储过程中的一条 RETURN 语句返回该值,因此,用于访问该值的参数类型是 ReturnValue 参数。

ReturnValue 参数与其他类型的参数之间有一个重要的不同之处。 在通常情况下,在 ADO.NET 中为 Command对象配置各个参数的顺序无关紧要。 各个参数的名称可用于将其与存储过程中的相应参数进行匹配。 然而,对于 ReturnValue参数而言,它必须 是该列表中的第一个参数。

这就意味着,当您为一个 Command 对象配置 ReturnValue参数时,必须在代码中首先配置这个参数,这样它就可以获取集合中的第一个数字索引。 如果您首先配置任何其他的参数,ReturnValue参数将不会正常运行。

为了演示带返回值的存储过程的使用,我们将执行一个示例 — 将一条记录插入 Northwind Products 表。 这个表被设置为利用一个 Identity 列自动生成新的产品 ID。 不巧的是,Northwind 示例数据库中并不包含一个可完成我们想要的操作的存储过程,因此,在进行本示例的剩余部分之前,我们必须在数据库中插入相应的存储过程。

转至 Visual Studio .NET 中的 Server Explorer。 打开 SQL Server 节点,然后打开相应 SQL Server 实例的节点。 然后打开 Northwind 数据库节点。

右键单击 Stored Procedures 节点,然后选择 New Stored Procedure。 在所显示的编辑窗口中,用以下文本替换其中所有的文本:

 ALTER PROCEDURE dbo.MSDNInsertProduct
(
@ProductName nvarchar(40),
@SupplierID int,
@CategoryID int,
@QuantityPerUnit nvarchar(20),
@UnitPrice money,
@UnitsInStock smallint,
@UnitsOnOrder smallint,
@ReorderLevel smallint,
@Discontinued bit
)
AS
declare @ProductID int
SET NOCOUNT OFF;
INSERT INTO Products(ProductName, SupplierID, CategoryID, QuantityPerUnit,
UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued) VALUES
(@ProductName, @SupplierID, @CategoryID, @QuantityPerUnit, @UnitPrice,
@UnitsInStock, @UnitsOnOrder, @ReorderLevel, @Discontinued);
SELECT @ProductID = @@IDENTITY
RETURN @ProductID

现在,关闭编辑窗口,将询问您是否想要保存更改,单击 Yes。 该存储过程已被保存在数据库中,且将被命名为 MSDNInsertProduct

现在,我们就可以编写代码来使用该存储过程了。 创建一个新的 Windows 应用程序,在空白 Form1 上,放置一个 DataGrid,并将其定位到所有四条边。 另外,添加名为 btnFillbtnInsertProduct的两个按钮。 将 btnFillText 属性设置为 Fill 而将 btnInsertProductText 属性设置为 Insert Product

btnFill 的 click 事件中,放入以下代码:

 Dim sConnectionString As String = _
"server=localhost;uid=sa;pwd=;database=Northwind"
Dim sSQL As String = "SELECT * FROM Products"
Dim daGetProducts As New SqlDataAdapter(sSQL, sConnectionString)
Dim dsProducts As New DataSet()
daGetProducts.Fill(dsProducts, "Products")
DataGrid1.DataSource = dsProducts

这段代码与本文中前面所见的代码几乎完全相同,因此我们不再讨论。 如有必要,不要忘记更改连接字符串,并将 SQLClient 命名空间的 Imports 语句放在该项目代码的开始位置处。 然后将以下代码放到 btnInsertProductClick事件中。

 Dim sConnectionString As String = _
"server=localhost;uid=sa;pwd=;database=Northwind"
Dim cnNorthwind As New SqlConnection(sConnectionString)
Dim cmdInsertProduct As New SqlCommand("MSDNInsertProduct", cnNorthwind)
cmdInsertProduct.CommandType = CommandType.StoredProcedure
' Set up parameters for stored procedure
cmdInsertProduct.Parameters.Add(New SqlParameter("@RETURN_VALUE", SqlDbType.Int, 4, "ProductID"))
cmdInsertProduct.Parameters("@RETURN_VALUE").Direction = ParameterDirection.ReturnValue
cmdInsertProduct.Parameters.Add(New SqlParameter("@ProductName", _
SqlDbType.NVarChar, 40, "ProductName"))
cmdInsertProduct.Parameters.Add(New SqlParameter("@SupplierID", _
SqlDbType.Int, 4, "SupplierID"))
cmdInsertProduct.Parameters.Add(New SqlParameter("@CategoryID", _
SqlDbType.Int, 4, "CategoryID"))
cmdInsertProduct.Parameters.Add(New SqlParameter("@QuantityPerUnit", _
SqlDbType.NVarChar, 20, "QuantityPerUnit"))
cmdInsertProduct.Parameters.Add(New SqlParameter("@UnitPrice", _
SqlDbType.Money, 8, "UnitPrice"))
cmdInsertProduct.Parameters.Add(New SqlParameter("@UnitsInStock", _
SqlDbType.SmallInt, 2, "UnitsInStock"))
cmdInsertProduct.Parameters.Add(New SqlParameter("@UnitsOnOrder", _
SqlDbType.SmallInt, 2, "UnitsOnOrder"))
cmdInsertProduct.Parameters.Add(New SqlParameter("@ReorderLevel", _
SqlDbType.SmallInt, 2, "ReorderLevel"))
cmdInsertProduct.Parameters.Add(New SqlParameter("@Discontinued", _
SqlDbType.Bit, 1, "Discontinued"))
Dim daInsertProduct As New SqlDataAdapter()
daInsertProduct.InsertCommand = cmdInsertProduct
Dim dsProducts As DataSet = CType(DataGrid1.DataSource, DataSet)
Dim drNewProduct As DataRow
drNewProduct = dsProducts.Tables("Products").NewRow
drNewProduct.Item("ProductName") = "Billy's Sesame Oil"
drNewProduct.Item("SupplierID") = 4
drNewProduct.Item("CategoryID") = 7
drNewProduct.Item("QuantityPerUnit") = "6 10oz bottles"
drNewProduct.Item("UnitPrice") = 69
drNewProduct.Item("UnitsInStock") = 12
drNewProduct.Item("UnitsOnOrder") = 0
drNewProduct.Item("ReorderLevel") = 6
drNewProduct.Item("Discontinued") = False
dsProducts.Tables("Products").Rows.Add(drNewProduct)
daInsertProduct.Update(dsProducts.Tables("Products"))
MsgBox(drNewProduct.Item("ProductID"))

除了用于为返回值配置参数的各行外,这段代码与我们前面所见的代码很相似。 请注意,这是第一个参数,设置此参数是为了将返回值放回到 ProductID 字段。

用于将新行插入数据集的代码是标准的 ADO.NET 代码,因此我们将不再详述。 它为产品记录创建一个具有合适结构的新行(利用 products DataTableNewRow 方法),将数据放入行中,最后将该行加入 products DataTable 的 Rows 集合中。

现在,运行该程序以进行测试。 单击 Fill 按钮,但是不对网格中的任何数据进行更改。 然后按 Insert Product按钮。 将插入一条对应于 Billy's Sesame Oil 的新产品记录,且一个消息框将显示针对该记录返回的 ProductID。 您也可以在网格中打开 Products 表,滚动浏览到底部,即可注意到已添加的新记录。

使用 Server Explorer 编写参数代码

以上的代码编写起来乏味而重复。 然而,DataAdapter 配置向导提示实际上 Visual Studio 可以替我们编写这些代码。 DataAdapter 配置向导为一个完整配置中的所有四个存储过程(SelectUpdateInsertDelete 各一个)生成代码。 假设您正需要如上例所示的某一个存储过程的代码,那该如何呢? 您仍然可以走一条捷径。 要获得只是与一个存储过程接口的预编写代码,只需展开 Server Explorer 以显示您想要使用的存储过程,然后将该存储过程拖到设计界面上即可。 您将看到只是为该存储过程创建的 DataAdapterCommand对象,且该代码的设计器部分将包含为该存储过程配置各个参数所需的全部代码。 您可原封不动地使用该代码,也可根据自己的需要而复制并改写该代码。

小结

本文中的各个示例仍然是演示软件,但是至少这些示例向您展示的使用存储过程的相关信息足以使您开始编写自己的软件。 当然了,您需要理解您将要使用的存储过程,并且可能需要询问数据库管理员 (DBA) 或其他小组成员以获得这些信息。

对于复杂系统而言,存储过程具有很多优点。 我希望您已从本文中学习到足够的知识,而不会对开始使用存储过程而心存畏惧。 在最初的一些尝试中,您可能想要使用 DataAdapter 向导或者 Server Explorer 为您编写大多数代码,但如果您能够在需要的时候编写自己的访问代码,那么您工作起来将事半功倍。

转到原英文页面


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多