适用于: .NET Framework
.NET
.NET Standard
通过提供类型检查和验证,命令对象可使用参数来将值传递给 SQL 语句或存储过程。 与命令文本不同,参数输入被视为文本值,而不是可执行代码。 此行为可帮助抵御“SQL 注入”攻击,这种攻击的攻击者会在 SQL 语句中插入危及服务器安全的命令。
参数化命令还可提高查询执行性能,因为它们可帮助数据库服务器将传入命令与适当的缓存查询计划进行准确匹配。 有关详细信息,请参阅执行计划的缓存和重用和重用参数和执行计划。 除具备安全和性能优势外,参数化命令还提供一种用于组织传递到数据源的值的便捷方法。
DbParameter 对象可以通过使用其构造函数来创建,或者也可以通过调用 DbParameterCollection 集合的 Add 方法以将该对象添加到 DbParameterCollection 来创建。
Add 方法将构造函数实参或现有形参对象用作输入,具体取决于数据提供程序。
提供 ParameterDirection 属性
在添加参数时,您必须为输入参数以外的参数提供一个 ParameterDirection 属性。 下表显示了可用于 ParameterDirection 枚举的 ParameterDirection 值。
| 成员名称 | 描述 |
|---|---|
| Input | 该参数为输入参数。 此值为默认值。 |
| InputOutput | 该参数可执行输入和输出。 |
| Output | 该参数为输出参数。 |
| ReturnValue | 该参数表示从某操作(如存储过程、内置函数或用户定义的函数)返回的值。 |
使用参数占位符
参数占位符的语法取决于数据源。 Microsoft SqlClient Data Provider for SQL Server 以不同方式处理命名和指定参数以及参数占位符。 SqlClient 数据提供程序以 @parametername 格式使用命名参数。
指定参数数据类型
参数的数据类型是 Microsoft SqlClient Data Provider for SQL Server 所特有的。 指定类型后,会先将 Parameter 的值转换为 Microsoft SqlClient Data Provider for SQL Server 的 SQL Server 类型,然后再将该值传递给数据源。 也可以通过通用的方式指定 Parameter 的类型,方法是将 DbType 对象的 Parameter 属性设置为特定的 DbType。
Parameter 对象的 Microsoft SqlClient Data Provider for SQL Server 类型是根据 Value 对象 Parameter 的 .NET Framework 类型或 DbType 对象的 Parameter 推断出来的。 下表显示了根据作为 Parameter 值传递的对象或指定的 Parameter 推断出的 DbType类型。
| .NET 类型 | DbType | SqlDbType |
|---|---|---|
| Boolean | Boolean |
Bit |
| Byte | Byte |
TinyInt |
byte[] |
Binary |
VarBinary。 如果字节数组大于 VarBinary 的最大大小(8000 字节),此隐式转换将失败。 对于大于 8000 字节的字节数组,请显式设置 SqlDbType。 |
| Char | 不支持根据 char 推断出 SqlDbType。 | |
| DateTime | DateTime |
DateTime |
| DateTimeOffset | DateTimeOffset |
DateTimeOffset 在 SQL Server 2008 中。 在低于 SQL Server 2008 的 SQL Server 版本中,不支持从 SqlDbType 推断 DateTimeOffset。 |
| Decimal | Decimal |
Decimal |
| Double | Double |
Float |
| Single | Single |
Real |
| Guid | Guid |
UniqueIdentifier |
| Int16 | Int16 |
SmallInt |
| Int32 | Int32 |
Int |
| Int64 | Int64 |
BigInt |
| Object | Object |
Variant |
| String | String |
NVarChar。 如果字符串大于 NVarChar 的最大大小(4000 字符),此隐式转换将失败。 对于大于 4000 个字符的字符串,请显式设置 SqlDbType。 |
| TimeSpan | Time |
Time 在 SQL Server 2008 中。 在低于 SQL Server 2008 的 SQL Server 版本中,不支持从 SqlDbType 推断 TimeSpan。 |
| UInt16 | UInt16 |
不支持从 SqlDbType 推断 UInt16。 |
| UInt32 | UInt32 |
不支持从 SqlDbType 推断 UInt32。 |
| UInt64 | UInt64 |
不支持从 SqlDbType 推断 UInt64。 |
AnsiString |
VarChar |
|
AnsiStringFixedLength |
Char |
|
Currency |
Money |
|
Date |
Date 在 SQL Server 2008 中。 在低于 SQL Server 2008 的 SQL Server 版本中,不支持从 SqlDbType 推断 Date。 |
|
SByte |
不支持从 SqlDbType 推断 SByte。 |
|
StringFixedLength |
NChar |
|
Time |
Time 在 SQL Server 2008 中。 在低于 SQL Server 2008 的 SQL Server 版本中,不支持从 SqlDbType 推断 Time。 |
|
VarNumeric |
不支持从 SqlDbType 推断 VarNumeric。 |
|
| 用户定义的类型(带有 SqlUserDefinedAggregateAttribute 的对象) | SqlClient 始终返回对象 | 如果 SqlDbType.Udt 存在,则为 SqlUserDefinedTypeAttribute;否则为 Variant |
| SqlJson | String |
SqlDbType.Json |
| SqlVector<T> | Binary |
SqlDbType.Vector |
注意
从小数转换到其他类型是缩窄转换,这种转换会将小数值舍入到最近的接近零的整数值。 如果转换结果不能在目标类型中表示,则会引发 OverflowException。
注意
JSON
在传递类型Value参数string时,将 SqlDbType 设置为 Json。 否则,SqlDbType 默认为 Nvarchar.
向量
对于SQL矢量数据类型,必须指定一个类型的Microsoft.Data.SqlTypes.SqlVector<T>参数Value。 参数 Size 和向量维度是从参数 Value推断的。 忽略参数 Size 。
注意
将空参数值发送到服务器时,必须指定 DBNull,而不是 null(在 Visual Basic 中为 Nothing)。 系统中的 null 值是一个不具有任何值的空对象。
DBNull 用于表示 null 值。
获取参数信息
还可以使用 DbCommandBuilder 类从存储过程派生参数。
SqlCommandBuilder 类提供静态方法,DeriveParameters,它自动填充使用存储过程中的参数信息的命令对象的参数集合。
DeriveParameters 会覆盖此命令的任何现有参数信息。
注意
派生参数信息会影响性能,因为它需要对数据源进行额外的往返访问,以检索信息。 如果参数信息在设计时是已知的,则可以通过显式设置参数来提高应用程序的性能。
有关详细信息,请参阅使用 CommandBuilders 生成命令。
对 SqlCommand 和存储过程使用参数
在数据驱动的应用程序中,存储过程具有许多优势。 通过使用存储过程,可将数据库操作封装在单个命令中,实现优化来获得最佳性能,并增强获得额外的安全性。 尽管可以通过在 SQL 语句中传递后接参数自变量的存储过程名称来调用相应的存储过程,但如果使用 ADO.NET Parameters 对象的 DbCommand 集合,则可以让你更为明确地定义存储过程参数,并访问输出参数和返回值。
注意
参数化语句通过 sp_executesql, 在服务器上执行,从而复用查询计划。
sp_executesql 批处理命令中的本地光标或变量对于调用 sp_executesql的批处理命令是不可见的。 数据库上下文中的更改只持续到 sp_executesql 语句的结尾。 有关详细信息,请参阅 sp_executesql (Transact-SQL)。
对 SqlCommand 使用参数以执行 SQL Server 存储过程时,添加到 Parameters 集合中的参数的名称必须与存储过程中参数标记的名称相匹配。 用于 SQL Server 的 Microsoft SqlClient 数据提供程序不支持用于向 SQL 语句或存储过程传递参数的问号 (?) 占位符。 它将存储过程中的参数视为命名参数,并搜索匹配的参数标记。 例如,通过使用名为 CustOrderHist 的参数定义 @CustomerID存储过程。 您的代码在执行该存储过程时,它也必须使用名为 @CustomerID的参数。
CREATE PROCEDURE dbo.CustOrderHist @CustomerID varchar(5)
示例
此示例演示了如何调用 Northwind 示例数据库中的 SQL Server 存储过程。 存储过程的名称为 dbo.SalesByCategory ,它具有名为 @CategoryName 的输入参数,其数据类型为 nvarchar(15)。 该代码在 using 代码块内创建一个新 SqlConnection ,以便在过程结束时释放连接。 会创建 SqlCommand 和 SqlParameter 对象,并设置其属性。
SqlDataReader 会执行 SqlCommand 并从存储过程返回结果集,以在控制台窗口中显示相关输出。
注意
您可以选择使用任一重载构造函数在一个语句中设置多个属性,而不是创建 SqlCommand 和 SqlParameter 对象,然后在各个语句中设置属性。
static void GetSalesByCategory(string connectionString,
string categoryName)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Create the command and set its properties.
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = "SalesByCategory";
command.CommandType = CommandType.StoredProcedure;
// Add the input parameter and set its properties.
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "@CategoryName";
parameter.SqlDbType = SqlDbType.NVarChar;
parameter.Direction = ParameterDirection.Input;
parameter.Value = categoryName;
// Add the parameter to the Parameters collection.
command.Parameters.Add(parameter);
// Open the connection and execute the reader.
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine("{0}: {1:C}", reader[0], reader[1]);
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
}
}