Подтвердить что ты не робот

SqlParameter с Nullable значением дает ошибку во время ExecuteNonQuery?

У меня есть sql-запрос с параметром, который может быть нулевым в базе данных (Sql Server). Метод обновления работает нормально до тех пор, пока этот пользователь не поместит пробел в поле, это приведет к нулевому значению для объекта DataTime (этот объект является нулевым). Проблема в том, что 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);

Я пытаюсь преобразовать нулевое значение дня рождения в 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'

Любая идея?

4b9b3361

Ответ 1

Типы несовместимы. Попробуйте что-то вроде этого:

dbParam_au_id.Value = (object)birthday ?? DBNull.Value;

Ответ 2

Если класс SqlParameter был правильно написан в первый раз... значение С# null будет обрабатываться как DBNull.Value. Это было бы интуитивно, поэтому OF COURSE, значение 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

Как вы можете видеть, в этом методе (и перегрузках), где значение уже набранные в качестве объекта, я использую "значение?? DBNull.Value" выражение для обеспечения нулевого = DBNull.Value правила.

Теперь, когда вы передаете нулевые ссылки на объекты или типы с нулевым значением без каких-либо значений в ваш метод AddParameter, вы получаете ожидаемое, интуитивное поведение, когда DBNull.Value передается в запрос.

Я не могу себе представить, почему API был реализован так, как он есть, потому что если бы я хотел, чтобы параметр игнорировался, я бы не добавил его, а затем установил его значение равным null. Я бы или НЕ добавил его в первую очередь, или я бы УДАЛИСЬ его из SqlParameterCollection. Если я ДОБАВЛЯЮТ параметр и SET его значение (даже если установлено значение null), я ожидаю, что он будет использоваться в запросе, я ожидаю, что значение null будет означать нулевое значение.

Я слышал, что они не выполнили это "правильный" путь для повышения производительности, но это смешно, как показано, так как вызов метода SqlParameterCollection.AddWithValue преобразует все на объект в любом случае, и преобразование Nullable экземпляра без значения к нулевому объекту является неотъемлемой частью языка С#, которая вообще не является хитом производительности. Microsoft действительно должна это исправить.