Присвоить ноль для SqlParameter


189

Следующий код выдает ошибку - «Нет неявного преобразования из DBnull в int».

SqlParameter[] parameters = new SqlParameter[1];    
SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);
planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex;
parameters[0] = planIndexParameter;

4
Вам нужно привести AgeItem.AgeIndex к объекту, я думаю ... stackoverflow.com/questions/202271/… (кстати, почему ==в конце 3-й строки?)
Грег,

Ответы:


341

Проблема заключается в том, что ?:оператор не может определить тип возвращаемого intзначения, поскольку вы либо возвращаете значение, либо значение типа DBNull, которые несовместимы.

Конечно, вы можете привести экземпляр AgeIndex к типу, objectкоторый удовлетворял бы ?:требованию.

Вы можете использовать ??оператор слияния нуля следующим образом

SqlParameter[] parameters = new SqlParameter[1];     
SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);
planIndexParameter.Value = (object)AgeItem.AgeIndex ?? DBNull.Value;
parameters[0] = planIndexParameter; 

Вот цитата из документации MSDN для ?:оператора, которая объясняет проблему

Либо тип first_expression и second_expression должен быть одинаковым, либо должно существовать неявное преобразование из одного типа в другой.


Почему не возникает исключение при попытке привести null к объекту? Я думаю, что это должно бытьAgeItem.AgeIndex as object
Нильс Бринч

@Niels Brinch, не было бы исключения, потому что null - это объект, и если вы не пытаетесь разыменовать его, это совершенно законно. Однако в этом примере это не нулевое приведение к объекту, это DBNull.Value, который на самом деле является типом значения. ?? Оператор говорит: «Если AgetItem.AgeIndex равен нулю, тогда возвращать DBNull.Value, в противном случае повторно вызывать AgeItem.AgeIndex», тогда ответ приводится к объекту. Смотрите ноль оператор объединения для получения более подробной информации. msdn.microsoft.com/en-us/library/ms173224.aspx
Крис Тейлор

3
Технически, ваше решение с помощью нуль-сливающихся оператора ??является то же решением , как если бы вы использовали регулярные тройной ?:- вам все равно нужно гипс AgeItem.AgeIndexк объекту: planIndexParameter.Value = AgeItem.AgeIndex.HasValue ? (object)AgeItem.AgeIndex : DBNull.Value;.
newfurniturey

Если бы вы использовали обычную троичную комбинацию ?:для сравнения конкретных типов, тогда приведение всего выражения не сработает. Вы должны разыграть не-dbnull параметр следующим образом:someID == 0 ? DBNull.Value : (object)someID
ингридиент_15939

Это верно, но если вам нужно использовать нулевое значение в качестве входного параметра функции, в результате которой используется SqlParameter, и если оно нулевое, у вас есть ошибка, этот способ не работает, и вы должны использовать простой способ If-Else. например: sample.Text.Trim ()! = ""? func (sample.Text): DBNull.Value; не будет работать как? а ??
QMaster

105

Принятый ответ предполагает использование броска. Однако большинство типов SQL имеют специальное поле Null, которое можно использовать, чтобы избежать этого приведения.

Например, SqlInt32.Null«Представляет DBNull, который может быть назначен этому экземпляру класса SqlInt32».

int? example = null;
object exampleCast = (object) example ?? DBNull.Value;
object exampleNoCast = example ?? SqlInt32.Null;

2
Предложение выглядело многообещающе, поэтому я попробовал «System.Data.SqlTypes.SqlString.Null», но оно не работает. Он помещает в поле фактическую строку «Null» ('N', 'u', 'l', 'l'), оставляя пустым значение true (null). Тем не менее, старый 2010 "принятый ответ", который использует приведение с (объект) ?? DBNull.Value работает правильно. (Поставщиком ADO.NET, который я использовал, был SQLite, но я не уверен, имеет ли это значение.) Я предлагаю, чтобы другие тщательно протестировали совет Брайана, чтобы убедиться, что нулевое поведение работает должным образом.
JasDev

6
@JasDev: Я смутно припоминаю, как описывал этот трюк в комментарии высокопоставленному пользователю (думаю, Марк Грэвелл), и мне говорили, что он работает только на Microsoft SQL Server.
Брайан

@JasDev, провайдер будет тем отличием, что это работает в SQL Server, как указывает Мозг.
Lankymart

Этот ответ заменяет только явное приведение к объекту неявным. One. В примере кода exampleNoCastобъявлен объект, поэтому приведение к объекту все еще происходит. Если, как и в коде OP, значение присваивается непосредственно SqlParameter.Value, который также имеет тип объекта, тогда вы все равно получите приведение.
Скотт

31

Вам необходимо передать DBNull.Valueкак пустой параметр в SQLCommand, если в хранимой процедуре не указано значение по умолчанию (если вы используете хранимую процедуру). Наилучший подход - назначить DBNull.Valueлюбой пропущенный параметр перед выполнением запроса, и следующий foreach сделает эту работу.

foreach (SqlParameter parameter in sqlCmd.Parameters)
{
    if (parameter.Value == null)
    {
        parameter.Value = DBNull.Value;
    }
}

В противном случае измените эту строку:

planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex;

Следующим образом:

if (AgeItem.AgeIndex== null)
    planIndexParameter.Value = DBNull.Value;
else
    planIndexParameter.Value = AgeItem.AgeIndex;

Потому что вы не можете использовать разные типы значений в условных выражениях, так как DBNull и int отличаются друг от друга. Надеюсь, это поможет.


Этот ответ действительно хорош, потому что он дает примеры во всех отношениях. Мне нравится первый подход, я обычно использую EF, но в этом требовании не могу этого сделать, и это экономит мне много времени. Спасибо!
Леандро

23

С одной строкой кода попробуйте это:

var piParameter = new SqlParameter("@AgeIndex", AgeItem.AgeIndex ?? (object)DBNull.Value);

5

Попробуй это:

SqlParameter[] parameters = new SqlParameter[1];    
SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);

planIndexParameter.IsNullable = true; // Add this line

planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex== ;
parameters[0] = planIndexParameter;

5

Если вы используете условный (троичный) оператор, компилятору требуется неявное преобразование между обоими типами, в противном случае вы получите исключение.

Таким образом, вы можете исправить это, приведя один из обоих к System.Object:

planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : (object) AgeItem.AgeIndex;

Но так как результат не очень хорош, и вы всегда должны помнить это приведение, вы можете использовать такой метод расширения:

public static object GetDBNullOrValue<T>(this T val)
{
    bool isDbNull = true;
    Type t = typeof(T);

    if (Nullable.GetUnderlyingType(t) != null)
        isDbNull = EqualityComparer<T>.Default.Equals(default(T), val);
    else if (t.IsValueType)
        isDbNull = false;
    else
        isDbNull = val == null;

    return isDbNull ? DBNull.Value : (object) val;
}

Тогда вы можете использовать этот краткий код:

planIndexParameter.Value = AgeItem.AgeIndex.GetDBNullOrValue();

1

На мой взгляд, лучше сделать это с помощью свойства Parameters класса SqlCommand :

public static void AddCommandParameter(SqlCommand myCommand)
{
    myCommand.Parameters.AddWithValue(
        "@AgeIndex",
        (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex);
}

Но если значение равно DBNull.Value, ADO.NET может быть довольно сложно угадать, какой это может быть SqlDbType ........ это удобно - но немного опасно ...
marc_s

1

Подумайте об использовании доступной структуры Nullable (T). Это позволит вам устанавливать значения только в том случае, если они у вас есть, и ваши объекты SQL Command будут распознавать обнуляемое значение и обрабатываться соответствующим образом без каких-либо хлопот с вашей стороны.


1
if (_id_categoria_padre > 0)
{
    objComando.Parameters.Add("id_categoria_padre", SqlDbType.Int).Value = _id_categoria_padre;
}
else
{
    objComando.Parameters.Add("id_categoria_padre", DBNull.Value).Value = DBNull.Value;
}

0

Попробуй это:

if (AgeItem.AgeIndex != null)
{
   SqlParameter[] parameters = new SqlParameter[1];
   SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);
   planIndexParameter.Value = AgeItem.AgeIndex;
   parameters[0] = planIndexParameter;
}

Другими словами, если параметр имеет значение null, просто не отправляйте его в свой хранимый процесс (при условии, конечно, что хранимый процесс принимает нулевые параметры, что подразумевается в вашем вопросе).


Но теперь вы просто опускаете параметр - я очень сомневаюсь, что хранимая процедура будет рада этому ... скорее всего, вызов не удастся, сообщив, что "значение для параметра @AgeIndex не предоставлено, что ожидалось" .... .
marc_s

Вот это да. Суровая. Просто запишите сохраненный процесс в значение по умолчанию, если параметр не передан (@AgeIndex int = 0). Бывает все время. Клиент может либо принять значение по умолчанию, либо переопределить его, передав параметр. Почему отрицательный голос?
Flipster

0

попробуйте что-то вроде этого:

if (_id_categoria_padre > 0)
{
    objComando.Parameters.Add("id_categoria_padre", SqlDbType.Int).Value = _id_categoria_padre;
}
else
{
    objComando.Parameters.Add("id_categoria_padre", DBNull.Value).Value = DBNull.Value;
}

0
int? nullableValue = null;
object nullableValueDB
{
   get{
       if(nullableValue==null)
          return DBNull.Value;
       else
          return (int)nullableValue;
   }
}

Я так решаю.


0
if (AgeItem.AgeIndex== null)  
    cmd.Parameters.Add(new SqlParameter("ParaMeterName", SqlDbType.DateTime).Value = DBNull);  
else  
    cmd.Parameters.Add(new SqlParameter("ParaMeterName", SqlDbType.DateTime).Value = AgeItem.AgeIndex);

0

Это то, что я просто делаю ...

        var PhoneParam = new SqlParameter("@Phone", DBNull.Value);
        if (user.User_Info_Phone != null)
        {
            PhoneParam.SqlValue = user.User_Info_Phone;
        }

        return this.Database.SqlQuery<CustLogonDM>("UpdateUserInfo @UserName, @NameLast, @NameMiddle, @NameFirst, @Address, @City, @State, @PostalCode, @Phone",
            UserNameParam, NameLastParam, NameMiddleParam, NameFirstParam, AddressParam, CityParam, StateParam, PostalParam, PhoneParam).Single();

0
            dynamic psd = DBNull.Value;

            if (schedule.pushScheduleDate > DateTime.MinValue)
            {
                psd = schedule.pushScheduleDate;
            }


            sql.DBController.RunGeneralStoredProcedureNonQuery("SchedulePush",
                     new string[] { "@PushScheduleDate"},
                     new object[] { psd }, 10, "PushCenter");

0

Простой метод расширения для этого будет:

    public static void AddParameter(this SqlCommand sqlCommand, string parameterName, 
        SqlDbType sqlDbType, object item)
    {
        sqlCommand.Parameters.Add(parameterName, sqlDbType).Value = item ?? DBNull.Value;
    }

0

Я использую простой метод с нулевой проверкой.

    public SqlParameter GetNullableParameter(string parameterName, object value)
    {
        if (value != null)
        {
            return new SqlParameter(parameterName, value);
        }
        else
        {
            return new SqlParameter(parameterName, DBNull.Value);
        }
    }

1
Это условная логика в обратном направлении? Должен ли DBNull.Value быть первым?
Марк Шультхайс

Конечно, есть. Исправлена. Спасибо.
Чжи Ан

0

Мой код, работающий в реальном проекте. Посмотрите на троичный оператор, прежде чем сделать sqlparameter для меня лучшим способом, без проблем:

    public bool Key_AddExisting
    (
          string clave
        , int? idHito_FileServer
        , int? idTipoDocumental_Almacen
        , string tipoExp_CHJ
        , int idTipoExp_Verti2
        , int idMov_Verti2
    )
    {
        List<SqlParameter> pars = new List<SqlParameter>()
        {
              new SqlParameter { ParameterName = "@Clave", Value = clave }
    LOOK -> , idHito_FileServer == null ? new SqlParameter { ParameterName = "@IdHito_FileServer", Value = DBNull.Value } : new SqlParameter { ParameterName = "@IdHito_FileServer", Value = idHito_FileServer }
    LOOK -> , idTipoDocumental_Almacen == null ? new SqlParameter { ParameterName = "@IdTipoDocumental_Almacen", Value = DBNull.Value } : new SqlParameter { ParameterName = "@IdTipoDocumental_Almacen", Value = idTipoDocumental_Almacen }
            , new SqlParameter { ParameterName = "@TipoExp_CHJ", Value = tipoExp_CHJ }
            , new SqlParameter { ParameterName = "@IdTipoExp_Verti2", Value = idTipoExp_Verti2 }
            , new SqlParameter { ParameterName = "@IdMov_Verti2", Value = idMov_Verti2 }
        };

        string sql = "INSERT INTO [dbo].[Enlaces_ClavesCHJ_MovimientosVerti2] " +
            "( " +
            "  [Clave] " +
            ", [IdHito_FileServer] " +
            ", [IdTipoDocumental_Almacen] " +
            ", [TipoExp_CHJ] " +
            ", [IdTipoExp_Verti2] " +
            ", [IdMov_Verti2] " +
            ") " +
            "VALUES" +
            "( " +
            "  @Clave" +
            ", @IdHito_FileServer" +
            ", @IdTipoDocumental_Almacen" +
            ", @TipoExp_CHJ" +
            ", @IdTipoExp_Verti2" +
            ", @IdMov_Verti2" +
            ")";

        return DbBasic.ExecNonQuery(ref this.conn, sql, pars);
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.