Призначте null 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/… (btw, чому ==в кінці 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", тоді відповідь буде передана об'єкту. Докладніші відомості див. У операторі null coalescing. msdn.microsoft.com/en-us/library/ms173224.aspx
Кріс Тейлор

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

Якщо ви використовували звичайний тернар ?:для порівняння, що стосується певного типу, то виведення всього виразу не вийде. Ви повинні передати параметр non-dbnull так:someID == 0 ? DBNull.Value : (object)someID
інгредієнт_15939

Це правда, але якщо вам потрібно використовувати значення "null" як вхідний параметр функції, результат якого споживає 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 року, який використовує "cast" (об'єкт) ?? DBNull.Value працює правильно. (Провайдер ADO.NET, який я використовував, був SQLite, але я не впевнений, чи це має значення.) Я пропоную іншим ретельно перевірити наконечник Брайана, щоб переконатися, що нульова поведінка працює як слід.
JasDev

6
@JasDev: Я туманно згадую, як описав цей трюк у коментарі високопоставленому користувачеві реп (я думаю, Марк Гравелл), і його сказали, що він працює лише на Microsoft SQL Server.
Брайан

@JasDev у постачальника буде різниця, яка працює в SQL Server, як відмовляється від мозку.
Ланкімарт

Ця відповідь замінює лише явний атрибут об’єкта implicit.one. У зразковому коді exampleNoCastоголошується об'єкт, тому передача об'єкта все ще відбувається. Якщо, як і в коді ОП, значення призначається безпосередньо SqlParameter.Value, який також є об'єктом типу, то ви все одно отримаєте команду.
Скотт

31

Вам потрібно пройти DBNull.Valueяк нульовий параметр в SQLCommand, якщо тільки не вказане значення за замовчуванням в межах збереженої процедури (якщо ви використовуєте збережену процедуру). Найкращим підходом є призначення DBNull.Valueбудь-якого відсутнього параметра перед виконанням запиту, а виконання наступних функцій зробить роботу.

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, просто не надсилайте його на ваш збережений додаток (якщо, звичайно, якщо збережений proc приймає нульові параметри, які є явними у вашому запитанні).


Але тепер ви просто опускаєте параметр - я дуже сумніваюся, що збережена процедура буде рада цьому .... швидше за все, виклик не вдасться зазначити "немає значення для наданого параметра @AgeIndex, який очікувався" .... .
marc_s

Ого. Різкий. Просто запишіть збережену процедуру за замовчуванням до значення, якщо параметр не передано (@AgeIndex int = 0). Відбувається весь час. Клієнт може або прийняти за замовчуванням, або змінити його, передавши параметр. Чому потік?
Фліпстер

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
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

Мій код, що працює в реальному проекті Подивіться, що потрійний оператор перед тим, як зробити sqlпараметр, це найкращий спосіб для мене, без проблем:

    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.