データベース(SQL Server)でnullになる可能性のあるパラメーターを持つSQLクエリがあります。 updateメソッドは、そのユーザーがフィールドに空白を入力するまで正常に機能します。これにより、DataTimeオブジェクトにnull値が生成されます(このオブジェクトはnull可能です)。問題は、dbCommand.ExecuteNonQuery();
の場合です。
このフィールドのパラメーターを作成する方法は次のとおりです。
IDataParameter dbParam_au_id = new SqlParameter();
dbParam_au_id.ParameterName = "@birthday";
dbParam_au_id.Value = birthday;
dbParam_au_id.DbType = DbType.DateTime;
dbCommand.Parameters.Add(dbParam_au_id);
私は誕生日のnull値を次のようにDBNull.Valueに変換しようとしました:
IDataParameter dbParam_au_id = new SqlParameter();
dbParam_au_id.ParameterName = "@birthday";
dbParam_au_id.Value = birthday??DBNull.Value;
dbParam_au_id.DbType = DbType.DateTime;
dbCommand.Parameters.Add(dbParam_au_id);
しかし、このコードはコンパイルされず、エラーが発生します:
エラー1演算子 '??'タイプ「System.DateTime?」のオペランドには適用できません。および「System.DBNull」
何か案が?
タイプは互換性がありません。次のようなものを試してください。
dbParam_au_id.Value = (object)birthday ?? DBNull.Value;
SqlParameterクラスが最初に正しく記述された場合、C#のnull値はDBNull.Valueとして処理されます。これは直感的であるため、OFCOURSEでSqlParameter値をnullに設定することは、機能的にはSqlParameterCollectionから削除することと同じです。
このばかげたAPI設計エラーを修正するには、独自のAddParameterメソッド(オーバーロードあり)を作成します。このメソッドは、SqlParameterCollection、String(パラメーター名)、およびObject(パラメーター値)を取ります。
#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ルールを適用します。
これで、nullオブジェクト参照または値のないnull許容型をAddParameterメソッドに渡すと、期待される直感的な動作が得られ、DBNull.Valueがクエリに渡されます。
APIがそのまま実装された理由は想像できません。パラメータを無視したい場合は、追加せずに値をnullに設定するからです。そもそも追加しないか、SqlParameterCollectionから削除します。パラメータを追加し、その値を設定すると(nullに設定されている場合でも)、クエリでUSEDになると予想され、nullはnull値を意味すると予想されます。
パフォーマンス上の理由から「正しい」方法で実装しなかったと聞きましたが、SqlParameterCollection.AddWithValueメソッドを呼び出すと、とにかくすべてがオブジェクトに変換され、値のないNullableインスタンスがnullに変換されるため、これはばかげています。オブジェクトはC#言語の本質的な部分であり、パフォーマンスへの影響はまったくありません。マイクロソフトは本当にこれを修正する必要があります。