กำหนดค่า 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)
Greg

คำตอบ:


341

ปัญหาคือ?:ผู้ประกอบการไม่สามารถกำหนดประเภทการส่งคืนได้เนื่องจากคุณส่งคืนintค่าหรือค่าชนิด DBNull ซึ่งไม่สามารถใช้งานร่วมกันได้

แน่นอนคุณสามารถสร้างอินสแตนซ์ของ AgeIndex ให้เป็นประเภทobjectที่จะตอบสนอง?:ความต้องการ

คุณสามารถใช้ตัวดำเนินการ??null-coalescing ดังต่อไปนี้

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 ต้องเหมือนกันหรือการแปลงโดยนัยต้องมีอยู่จากประเภทหนึ่งไปเป็นอีกประเภทหนึ่ง


ทำไมไม่มีข้อยกเว้นเมื่อพยายามส่งสัญญาณเป็นโมฆะเพื่อคัดค้าน ฉันคิดว่าควรเป็นAgeItem.AgeIndex as object
Niels Brinch

@ Niels Brinch คงไม่มีข้อยกเว้นเพราะ null เป็นวัตถุและตราบใดที่คุณไม่ลองใช้มันมันจะถูกกฎหมายอย่างสมบูรณ์ อย่างไรก็ตามในตัวอย่างนี้มันไม่ได้ถูกทิ้งให้เป็นวัตถุมันเป็น DBNull.Value ซึ่งจริงๆแล้วเป็นประเภทของค่า ?? โอเปอเรเตอร์บอกว่า 'ถ้า AgetItem.AgeIndex เป็นโมฆะแล้วส่งกลับ DBNull ค่าอื่นจะเปิดใช้งาน AgeItem.AgeIndex อีกครั้ง' ดังนั้นการตอบสนองจะถูกโยนไปยังวัตถุ ดูตัวดำเนินการรวมศูนย์ว่างสำหรับรายละเอียดเพิ่มเติม msdn.microsoft.com/en-us/library/ms173224.aspx
Chris Taylor

3
เทคนิควิธีการแก้ปัญหาของคุณโดยใช้ผู้ประกอบการด้วย null หลอมรวม??เป็นทางออกเดียวกับถ้าคุณมีการใช้ ternary ปกติ?:- คุณยังคงต้องโยนไปยังวัตถุที่:AgeItem.AgeIndex planIndexParameter.Value = AgeItem.AgeIndex.HasValue ? (object)AgeItem.AgeIndex : DBNull.Value;
เฟอร์นิเจอร์ใหม่

หากคุณต้องใช้ ternary ปกติ?:เพื่อทำการเปรียบเทียบเฉพาะประเภทการคัดเลือกการแสดงออกทั้งหมดจะไม่ทำงาน คุณต้องส่งพารามิเตอร์ที่ไม่ใช่ dbnull ดังนี้:someID == 0 ? DBNull.Value : (object)someID
ingredients_15939

นั่นเป็นจริง แต่ถ้าคุณต้องการใช้ค่าที่สามารถเป็นโมฆะเป็นพารามิเตอร์ทางเข้าของฟังก์ชันที่ใช้ SqlParameter และถ้าเป็นโมฆะคุณมีข้อผิดพลาดวิธีนี้ใช้ไม่ได้และคุณควรใช้วิธี If-Else แบบง่าย ๆ ตัวอย่างเช่น: sample.Text.Trim ()! = ""? func (ตัวอย่างข้อความ): 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') ลงในช่องแทนที่จะปล่อยให้มันว่างเปล่าด้วยจริง (null) อย่างไรก็ตาม 2010 เก่า "ตอบรับ" ที่ใช้กับ cast (วัตถุ) DBNull.Value ทำงานอย่างถูกต้อง (ผู้ให้บริการ ADO.NET ที่ฉันใช้คือ SQLite แต่ฉันไม่แน่ใจว่าสิ่งนั้นจะสร้างความแตกต่างได้หรือไม่) ฉันแนะนำให้ผู้อื่นทดสอบอย่างระมัดระวังเคล็ดลับของ Brian เพื่อให้แน่ใจว่าพฤติกรรมที่เป็นโมฆะทำงานได้ตามปกติ
JasDev

6
@JasDev: ฉันจำได้ว่าอธิบายเคล็ดลับนี้ในความคิดเห็นของผู้ใช้ตัวแทนระดับสูง (ฉันคิดว่า Marc Gravell) และได้รับการบอกว่าใช้งานได้กับ Microsoft SQL Server เท่านั้น
ไบรอัน

@ JasDev ผู้ให้บริการจะมีความแตกต่างในการทำงานของ SQL Server ตามที่ Brain point มีอยู่
Lankymart

คำตอบนี้แทนที่เฉพาะ cast to object ด้วย implicit.one ในโค้ดตัวอย่างexampleNoCastจะถูกประกาศวัตถุดังนั้นการโยนไปยังวัตถุยังคงเกิดขึ้น ถ้าเช่นเดียวกับในรหัสของ OP ค่าจะถูกกำหนดโดยตรงกับ SqlParameter.Value ซึ่งเป็นประเภทวัตถุคุณก็ยังได้รับการส่ง
สกอตต์

31

คุณต้องผ่านDBNull.Valueเป็นพารามิเตอร์ null ภายใน 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 แต่ในข้อกำหนดนี้ไม่สามารถทำได้และช่วยให้ฉันประหยัดเวลาได้มาก ขอบคุณ!
ลีอันโดร


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

หากคุณใช้ตัวดำเนินการตามเงื่อนไข (ternary) คอมไพเลอร์ต้องการการแปลงโดยนัยระหว่างทั้งสองประเภทมิฉะนั้นคุณจะได้รับข้อยกเว้น

ดังนั้นคุณสามารถแก้ไขได้โดยการเลือกหนึ่งใน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

ในความคิดของฉันวิธีที่ดีกว่าคือการทำเช่นนี้กับคุณสมบัติพารามิเตอร์ของคลาสSqlCommand :

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

แต่ถ้าค่าคือDBNull.ValueADO.NET อาจมีเวลาค่อนข้างยากที่จะเดาว่า SqlDbType ใดที่อาจเป็น ........ มันสะดวก - แต่ค่อนข้างอันตราย ....
marc_s

1

พิจารณาใช้โครงสร้าง Nullable (T) ที่มีอยู่ มันจะช่วยให้คุณตั้งค่าเฉพาะเมื่อคุณมีและวัตถุคำสั่ง SQL ของคุณจะรับรู้ค่าที่เป็นโมฆะและกระบวนการตามที่ไม่ยุ่งยากในตอนท้ายของคุณ


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

กล่าวอีกนัยหนึ่งถ้าพารามิเตอร์เป็นโมฆะอย่าส่งไปยัง proc ที่เก็บไว้ของคุณ (โดยแน่นอนว่า proc ที่เก็บไว้ยอมรับพารามิเตอร์ว่างซึ่งเป็นข้อสงสัยในคำถามของคุณ)


แต่ตอนนี้คุณเพียงแค่ละเว้นพารามิเตอร์ - ฉันสงสัยอย่างยิ่งว่าขั้นตอนการจัดเก็บจะมีความสุขกับเรื่องนี้ .... ส่วนใหญ่การโทรจะล้มเหลวที่ระบุว่า "ไม่มีค่าสำหรับพารามิเตอร์ @AgeIndex ที่ให้มาซึ่งคาดว่า" .... .
marc_s

ว้าว. รุนแรง เพียงเขียน proc ที่เก็บไว้เป็นค่าเริ่มต้นหากไม่มีการส่งผ่านพารามิเตอร์ (@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

ฉันใช้วิธีการง่ายๆด้วยการตรวจสอบ null

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

1
ตรรกะนั้นมีเงื่อนไขย้อนหลังหรือไม่ DBNull ควรเป็นค่าแรกหรือไม่
Mark Schultheiss

แน่นอนคือ แก้ไขแล้ว. ขอบคุณ
Zhi An

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);
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.