如果第一次正确编写了 SqlParameter 类... C# 空值将作为 DBNull.Value 处理。这很直观,因此将 SqlParameter 值设置为 null 在功能上等同于将其从 SqlParameterCollection 中删除。
要纠正这个荒谬的 API 设计错误,请创建您自己的 AddParameter 方法(带有重载),该方法接受一个 SqlParameterCollection、一个字符串(参数名称)和一个对象(参数值)。
#region Add by Name/Value.
/// <summary>
/// Adds an input parameter with a name and value. Automatically handles conversion of null object values to DBNull.Value.
/// </summary>
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param>
/// <param name="name">The name of the parameter to add.</param>
/// <param name="value">The value of the parameter to add.</param>
private static void AddParameter( SqlParameterCollection parameters, string name, object value )
{
parameters.Add( new SqlParameter( name, value ?? DBNull.Value ) );
}
/// <summary>
/// Adds a parameter with a name and value. You specify the input/output direction. Automatically handles conversion of null object values to DBNull.Value.
/// </summary>
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param>
/// <param name="name">The name of the parameter to add.</param>
/// <param name="value">The value of the parameter to add. If null, this is automatically converted to DBNull.Value.</param>
/// <param name="direction">The ParameterDirection of the parameter to add (input, output, input/output, or return value).</param>
private static void AddParameter( SqlParameterCollection parameters, string name, object value, ParameterDirection direction )
{
SqlParameter parameter = new SqlParameter( name, value ?? DBNull.Value );
parameter.Direction = direction;
parameters.Add( parameter );
}
#endregion
#region Add by Name, Type, and Value.
/// <summary>
/// Adds an input parameter with a name, type, and value. Automatically handles conversion of null object values to DBNull.Value.
/// </summary>
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param>
/// <param name="name">The name of the parameter to add.</param>
/// <param name="type">Specifies the SqlDbType of the parameter.</param>
/// <param name="value">The value of the parameter to add. If null, this is automatically converted to DBNull.Value.</param>
private static void AddParameter( SqlParameterCollection parameters, string name, SqlDbType type, object value )
{
AddParameter( parameters, name, type, 0, value ?? DBNull.Value, ParameterDirection.Input );
}
/// <summary>
/// Adds a parameter with a name, type, and value. You specify the input/output direction. Automatically handles conversion of null object values to DBNull.Value.
/// </summary>
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param>
/// <param name="name">The name of the parameter to add.</param>
/// <param name="type">Specifies the SqlDbType of the parameter.</param>
/// <param name="value">The value of the parameter to add. If null, this is automatically converted to DBNull.Value.</param>
/// <param name="direction">The ParameterDirection of the parameter to add (input, output, input/output, or return value).</param>
private static void AddParameter( SqlParameterCollection parameters, string name, SqlDbType type, object value, ParameterDirection direction )
{
AddParameter( parameters, name, type, 0, value ?? DBNull.Value, direction );
}
#endregion
#region Add by Name, Type, Size, and Value.
/// <summary>
/// Adds an input parameter with a name, type, size, and value. Automatically handles conversion of null object values to DBNull.Value.
/// </summary>
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param>
/// <param name="name">The name of the parameter to add.</param>
/// <param name="type">Specifies the SqlDbType of the parameter.</param>
/// <param name="size">Specifies the size of the parameter for parameter types of variable size. Set to zero to use the default size.</param>
/// <param name="value">The value of the parameter to add. If null, this is automatically converted to DBNull.Value.</param>
private static void AddParameter( SqlParameterCollection parameters, string name, SqlDbType type, int size, object value )
{
AddParameter( parameters, name, type, size, value ?? DBNull.Value, ParameterDirection.Input );
}
/// <summary>
/// Adds a parameter with a name, type, size, and value. You specify the input/output direction. Automatically handles conversion of null object values to DBNull.Value.
/// </summary>
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param>
/// <param name="name">The name of the parameter to add.</param>
/// <param name="type">Specifies the SqlDbType of the parameter.</param>
/// <param name="size">Specifies the size of the parameter for parameter types of variable size. Set to zero to use the default size.</param>
/// <param name="value">The value of the parameter to add. If null, this is automatically converted to DBNull.Value.</param>
/// <param name="direction">The ParameterDirection of the parameter to add (input, output, input/output, or return value).</param>
private static void AddParameter( SqlParameterCollection parameters, string name, SqlDbType type, int size, object value, ParameterDirection direction )
{
SqlParameter parameter;
if (size < 1)
parameter = new SqlParameter( name, type );
else
parameter = new SqlParameter( name, type, size );
parameter.Value = value ?? DBNull.Value;
parameter.Direction = direction;
parameters.Add( parameter );
}
#endregion
如您所见,在该方法(和重载)中,值已经作为对象键入,我使用“value ?? DBNull.Value”语句来强制执行 null = DBNull.Value 规则。
现在,当您将空对象引用或不带值的可空类型传递给 AddParameter 方法时,您会得到预期的直观行为,其中将 DBNull.Value 传递给查询。
我无法想象为什么 API 会按原样实现,因为如果我想忽略一个参数,我不会添加它然后将它的值设置为 null。我要么一开始就不添加它,要么从 SqlParameterCollection 中删除它。如果我添加一个参数,并设置它的值(即使设置为 null),我希望它在查询中使用,我希望 null 表示 null 值。
我听说他们出于性能原因没有以“正确”的方式实现它,但正如所证明的那样,这很荒谬,因为调用 SqlParameterCollection.AddWithValue 方法无论如何都会将所有内容转换为对象,并转换一个没有值的 Nullable 实例到 null 对象是 C# 语言的固有部分,根本不会影响性能。微软真的应该解决这个问题。