数据适配器命令中的参数
更新:2007 年 11 月
数据适配器的命令 -- 在 SelectCommand、InsertCommand、UpdateCommand 和 DeleteCommand 对象的 CommandText 属性中定义的命令 -- 通常包含参数。在运行时,参数用于向命令代表的 SQL 语句或存储过程传递值。
说明: |
---|
在 Visual Studio 的前一版本中,使用数据适配器在应用程序和数据库之间实现通信。尽管数据适配器仍是 .NET Framework 数据提供程序 (ADO.NET) 的一个主要组件,TableAdapter 是由设计器生成的组件,这些组件简化了在应用程序与数据库之间移动数据的过程。有关使用 TableAdapter 的更多信息,请参见 TableAdapter 概述。 |
参数在两种上下文中使用:
选择参数 -- 在产品应用程序中,经常只获取数据库中数据的一个子集。其做法是,使用包含 WHERE 子句的 SQL 语句或存储过程,该子句具有用于获得选择判据(在运行时获取)的参数。此外,当更新或删除记录时,将使用 WHERE 子句指出要更改的一条或多条记录。WHERE 子句中使用的值通常在运行时导出。
更新参数 -- 当更新现有记录或插入新记录时,已更改记录或新记录中列的值将在运行时建立。此外,开放式并发检查中使用的值也使用参数来建立。
说明: 对于 Oracle,在 SQL 语句或存储过程中使用命名参数时,必须在参数名称前加冒号 (:)。但是,当在代码中的其他地方引用命名参数时(例如,当调用 Add 时),不要在命名参数前加冒号 (:)。数据提供程序自动提供冒号。有关更多信息,请参见 OracleParameter 类。
选择参数
当选择记录来填充数据集时,经常在 WHERE 子句中包括一个或多个参数,以便能够在运行时指定要获取哪些记录。例如,用户可能会搜索书籍数据库以查找其键入网页的特定书名关键字。若要允许这样操作,可以将类似下面的 SQL 语句指定为 SelectCommand 的 CommandText 属性。参数用占位符(问号)或命名参数变量指示。涉及 OleDbCommand 和 OdbcCommand 对象的查询的参数使用问号;使用 SqlCommand 对象的查询使用以 @ 符号开头的命名参数,而 OracleCommand 对象使用以冒号 (:) 开头的命名参数。
使用占位符的查询可能如下所示:
SELECT BookId, Title, Author, Price from BOOKS
WHERE (Title LIKE ?)
使用 SqlCommand 命名参数的查询可能如下所示:
SELECT BookId, Title, Author, Price from BOOKS
WHERE (Title LIKE @title)
使用 OracleCommand 命名参数的查询可能如下所示:
SELECT BookId, Title, Author, Price from BOOKS
WHERE (Title LIKE :title)
在应用程序中,您提示用户输入书名关键字。然后设置参数值并运行命令。
说明: |
---|
有时,您可能需要获取数据库表的全部内容(例如在建立查找表时),但通常只需要获取所需数据以使应用程序效率较高。 |
在 Visual Studio 中,可以使用“查询生成器”生成带参数的 SQL 语句。如果从“服务器资源管理器”拖动元素,Visual Studio 可在某些情况下(但非所有情况下)配置参数,并且您需要手动完成配置。
更新参数
无论适配器的 SelectCommand 对象是否包含参数化命令,都始终执行 UpdateCommand、InsertCommand 和 DeleteCommand 属性的命令。
UpdateCommand 和 InsertCommand 属性的命令对于数据库中要更新的每一列都需要参数。此外,UpdateCommand 和 DeleteCommand 语句需要参数化的 WHERE 子句,它标识要更新的记录,其方式与经常配置 SelectCommand 对象的方式类似。
设想用户可以用其购买书籍的应用程序。用户在购物时将维护一个购物车(以数据表的形式实现)。在 ShoppingCart 表中,用户为要购买的每本书维持一条记录,以书籍 ID 和客户 ID 一起作为购物车记录的键。
当用户向购物车添加书籍时,应用程序可能调用 SQL INSERT 语句。在适配器中,该语句的语法可能如下所示:
INSERT INTO ShoppingCart
(BookId, CustId, Quantity)
Values (?, ?, ?)
三个问号代表参数占位符,它们将在运行时以客户 ID、书籍 ID 和数量的值填写。如果打算使用命名参数,同样的查询可能如下所示:
INSERT INTO ShoppingCart
(BookId, CustId, Quantity)
Values (@bookid, @custid, @quantity)
如果用户决定更改购物车中的某一项(例如更改其数量),则应用程序可能调用 SQL UPDATE 语句。该语句的语法可能如下所示:
UPDATE ShoppingCart
SET (BookId = ?, CustId = ?, Quantity = ?)
WHERE (BookId = ? AND CustId = ?)
或者如果打算使用命名参数,则可能如下所示:
UPDATE ShoppingCart
SET (BookId = @bookid, CustId = @custid, Quantity = @quantity)
WHERE (BookId = @bookid AND CustId = @custid)
在该语句中,SET 子句中的参数以已更改记录的更新值填写。WHERE 子句中的参数标识要更新哪条记录,并且以来自该记录的原始值填写。
用户还可以从购物车移除项。在此情况下,如果打算使用参数占位符,则应用程序可能调用具有如下语法的 SQL DELETE 语句:
DELETE FROM ShoppingCart
WHERE (BookId = ? AND CustId = ?)
或者如果打算使用命名参数,则可能如下所示:
DELETE FROM ShoppingCart
WHERE (BookId = @bookid AND CustId = @custid)
参数集合和参数对象
为允许您在运行时传递参数值,数据适配器的四个命令对象均支持 Parameters 属性。该属性包含单个参数对象的集合,这些对象与语句中的占位符一一对应。
下表显示了与每个数据适配器相对应的参数集合:
数据适配器 |
参数集合 |
---|---|
说明: |
---|
对于 Oracle,在 SQL 语句或存储过程中使用命名参数时,必须在参数名称前加冒号 (:)。但是,当在代码中的其他地方引用命名参数时(例如,当调用 Add 时),不要在命名参数前加冒号 (:)。Oracle .NET Framework 数据提供程序会自动提供冒号。 |
使用参数集合,您可省去必须手动将 SQL 命令构造为具有运行时值的字符串的麻烦。此外,还获得在参数中进行类型检查的好处。
如果使用“数据适配器配置向导”配置适配器,则为所有四个适配器命令自动建立和配置参数集合。如果从“服务器资源管理器”将元素拖动到窗体或组件上,则 Visual Studio 可以执行下列配置:
如果将表或某些列拖动到设计器上,则 Visual Studio 将生成一个不带参数的 SelectCommand 对象(明确地说是一条 SQL SELECT 语句),以及参数化的 UpdateCommand、InsertCommand 和 DeleteCommand 对象。如果希望 SelectCommand 对象语句有参数,则必须手动进行配置。
如果将存储过程拖动到设计器上,则 Visual Studio 将生成一个 SelectCommand 对象,带有存储过程所需的参数。但是,如果需要,您必须自己配置 UpdateCommand、InsertCommand 和 DeleteCommand 对象及其参数。
一般而言,如果想为适配器创建参数化查询,则应使用“数据适配器配置向导”。但是,如果需要,可以使用“属性”窗口手动配置参数。
参数集合的结构
命令的参数集合中的项与相应的命令对象所需的参数一一对应。如果命令对象是一条 SQL 语句,则集合中的项对应于该语句中的占位符(问号)。以下 UPDATE 语句需要有五个参数项的集合:
UPDATE ShoppingCart
SET (BookId = ?, CustId = ?, Quantity = ?)
WHERE (BookId = ? AND CustId = ?)
以下是带有命名参数的相同语句:
UPDATE ShoppingCart
SET (BookId = @bookid, CustId = @custid, Quantity = @quantity)
WHERE (BookId = @bookid AND CustId = @custid)
如果命令对象引用的是存储过程,则集合中的参数项数由该过程本身决定。参数与 SQL 语句中的占位符可能不完全对应。
在存储过程中,还可以对参数进行命名。在此情况下,参数在集合中的位置并不重要。相反,集合中的每个参数项都有一个 ParameterName 属性,用于使自身与存储过程中的相应参数匹配。
当手动配置参数集合时,必须确切理解哪些参数是存储过程需要的。许多存储过程返回值;如果是这样,该值在参数集合中传递回应用程序,因此您必须允许返回值。此外,某些存储过程包含多条 SQL 语句,您必须确保参数集合反映传递给过程中的全部语句的所有值。
如果参数不是命名参数(如在存储过程中那样),则集合中的项按位置映射到命令所需的参数。如果命令是一个存储过程并且返回值,则为该返回值保留集合中的第一项(零项)。
因此,可以根据集合中的索引位置引用单个参数对象。但是,参数对象还支持 ParameterName 属性,它提供一种独立于参数顺序来引用参数的方法。例如,下面两条语句可能等效(假定集合中的第二个参数被命名为 Title_Keyword):
' Encloses the keyword in SQL wildcard characters.
titleKeyword = "%" & txtTitleKeyword.Text & "%"
OleDbDataAdapter1.SelectCommand.Parameters(1).Value = titleKeyword
OleDbDataAdapter1.SelectCommand.Parameters("Title_Keyword").Value = titleKeyword
// Encloses the keyword in SQL wildcard characters.
string titleKeyword = "%" + txtTitleKeyword.Text + "%";
this.OleDbDataAdapter1.SelectCommand.Parameters[1].Value = titleKeyword;
this.OleDbDataAdapter1.SelectCommand.Parameters["Title_Keyword"].Value = titleKeyword;
编程时使用参数名通常比通过索引值引用参数好得多,因为它减少了在参数个数变化时的维护需要,并使您不必记忆存储过程是否返回值。通过名称而不是索引值来引用参数存在少许附加系统开销,但这可通过编程方便和应用程序的可维护性抵消。
建立参数值
建立参数值的方式有两种:
通过显式设置参数的 Value 属性。
通过将参数映射到数据集表中的列,以便可以在需要时从数据行提取值。
在填充数据集或调用命令时显式设置参数值(即对于选择参数)。例如,在上面搜索书籍的示例中,应用程序可能有一个文本框,用户在该框中输入书名关键字。然后,您在调用适配器的 Fill 方法之前,将参数的值显式设置为文本框的文本。完成该操作的代码可能如下所示,它在填充数据集之前将文本框的内容建立为参数。
' Encloses the keyword in SQL wildcard characters.
titleKeyword = "%" & txtTitleKeyword.Text & "%"
OleDbDataAdapter1.SelectCommand.Parameters("Title_Keyword").Value = titleKeyword
OleDbDataAdapter1.Fill(dsAuthors1)
// Encloses the keyword in SQL wildcard characters.
titleKeyword = "%" + txtTitleKeyword.Text + "%";
this.OleDbDataAdapter1.SelectCommand.Parameters["Title_Keyword"].Value = titleKeyword;
this.OleDbDataAdapter1.Fill(dsAuthors1);
映射的参数值在更新中使用。当调用适配器的 Update 方法时,该方法依次处理数据集表中的记录,分别为每条记录进行适当更新(更新、插入或删除)。在此情况下,参数值已可以作为数据集记录中的列使用。例如,当更新进程到达数据集表中的一个新记录(必须为其在数据库中调用 INSERT 语句)时,INSERT 语句的 VALUE 子句的值可以从该记录直接读出。
这些是典型方案,但不是唯一的几个方案。存储过程有时使用 out 参数或通过过程的返回值返回数据。如果是这样,返回值应映射到数据集表中的列。
也有可能显式设置更新参数。适配器支持 RowUpdating 事件,每次更新行时都将调用该事件。可以为该事件创建一个处理程序,并在其中设置参数值。这使您可以对参数值进行十分精确的控制,并可以执行诸如下面的处理:在参数值写入数据库记录之前动态创建参数值。