ไม่สามารถแคสต์ออบเจ็กต์ประเภท "System.DBNull" เพื่อพิมพ์ "System.String"


109

ฉันได้รับข้อผิดพลาดข้างต้นในแอปของฉัน นี่คือรหัสเดิม

public string GetCustomerNumber(Guid id)
{
     string accountNumber = 
          (string)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidmyApp, 
                          CommandType.StoredProcedure, 
                          "GetCustomerNumber", 
                          new SqlParameter("@id", id));
     return accountNumber.ToString();
 }

ฉันแทนที่ด้วย

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));
    if (accountNumber is System.DBNull)
    {
       return string.Empty;
    }
    else
    {
       return accountNumber.ToString();
    }
}

มีวิธีที่ดีกว่านี้หรือไม่?


2
คุณควรตรวจสอบคำตอบของ @ Rein จริงๆจะช่วยให้คุณประหยัดเวลาได้มากในระยะยาว
roman m

คำตอบ:


90

สามารถใช้แบบฟอร์มที่สั้นกว่า:

return (accountNumber == DBNull.Value) ? string.Empty : accountNumber.ToString()

แก้ไข: ยังไม่ได้ให้ความสนใจกับ ExecuteScalar มันจะคืนค่า null จริงๆถ้าไม่มีฟิลด์ในผลลัพธ์ที่กลับมา ดังนั้นใช้แทน:

return (accountNumber == null) ? string.Empty : accountNumber.ToString() 

3
ซึ่งจะใช้ไม่ได้ผล "accountNumber" ไม่ใช่ค่าฐานข้อมูล แต่เป็นอินสแตนซ์ "object" ของ Plain Old .NET แบบปกติ - คุณต้องตรวจสอบเทียบกับค่า "null" ปกติ DBNull.Value จะทำงานสำหรับ SqlDataReader หรือ SqlParameter - แต่ไม่ใช่สำหรับวัตถุนี้ที่นี่
marc_s

คุณพูดถูกฉันเริ่มเพิ่มประสิทธิภาพส่วนตรวจสอบเงื่อนไขไม่ได้ดูบรรทัดมาก่อน Mea culpa
ผู้ใช้

มีการพิมพ์ผิดในโพสต์ของคุณซึ่งฉันไม่สามารถแก้ไขได้เนื่องจากการแก้ไขต้องการให้เปลี่ยนตัวอักษร 6 ตัว ใครสามารถเปลี่ยน accountNumber.TosString () เป็น accountNumber ToString ()
เอริค

@marc_s ขึ้นอยู่กับเค้าโครง db / query คุณต้องตรวจสอบกับอย่างใดอย่างหนึ่งหรือแม้กระทั่งทั้งสองอย่าง หากสถานที่ไม่ตรงกับแถวใด ๆ ที่คุณจะได้รับnullถ้าแถวที่เลือกได้ในคอลัมน์ที่ส่งคืนค่าเป็นNULL System.DBNull
Alexander

ในกรณีแรกที่ @Alexander กล่าวถึง - ไม่ตรงกับแถวใด ๆ คุณสามารถพึ่งพา Convert ToString หรือวิธีการแปลงอื่น ๆ ได้หากคุณพอใจกับค่าที่ส่งคืนเมื่อแปลงจาก null: สตริงว่างสำหรับสตริง, 0 สำหรับค่าตัวเลข, เท็จ สำหรับบูลีน MinValue สำหรับ DateTime ... msdn.microsoft.com/en-us/library/vstudio/…
Jaime

199

ด้วยฟังก์ชันทั่วไปที่เรียบง่ายคุณสามารถทำให้สิ่งนี้ง่ายมาก เพียงทำสิ่งนี้:

return ConvertFromDBVal<string>(accountNumber);

โดยใช้ฟังก์ชัน:

public static T ConvertFromDBVal<T>(object obj)
{
    if (obj == null || obj == DBNull.Value)
    {
        return default(T); // returns the default value for the type
    }
    else
    {
        return (T)obj;
    }
}

1
ใช่ฟังก์ชั่นเช่นนี้เป็นทางออกเดียวที่ใช้ได้จริง ตรรกะในบรรทัดใด ๆ จะล้มเหลวหลังจากที่คุณคัดลอกและวางพันครั้ง :-)
Christian Hayter

3
สิ่งนี้จะใช้ไม่ได้ถ้าคุณลองแปลง 1 เป็นบูล (Convert ToBoolean (1) ใช้งานได้ดี)
roman m

@roman: ถ้าอย่างนั้นเราอยากจะมีการตรวจสอบเพิ่มเติม (ก่อนที่จะตรวจสอบค่าว่าง) ที่ตรวจสอบประเภทบูลีน ...
IAbstract

1
หากคุณต้องการหรือจำเป็นต้องใช้ฟังก์ชัน Convert แสดงว่าไม่ทำงาน มีหลายสถานการณ์ที่คุณอาจต้องการเปลี่ยนเป็นนักแสดงที่ชัดเจน @romanm ตั้งข้อสังเกตหนึ่งในนั้น อีกประการหนึ่งคือเมื่อคุณทำงานกับทศนิยมและดูแลเกี่ยวกับกลไกการปัดเศษต่าง ๆ ที่ Convert ToInt32 และ (int) ใช้ รอบก่อนหน้าเป็นค่าคู่ที่ใกล้ที่สุดในขณะที่การร่ายอย่างชัดเจนเพียงแค่ตัดทอนค่า: stackoverflow.com/questions/1608801/…ถ้าเป็นไปได้ฉันจะกำจัด NULL ออกจากการผสมโดยใช้ฟังก์ชัน T-SQL ISNULL
Jaime

2
@Jaime ฟังก์ชันนี้ควรจะทำหน้าที่เหมือนการส่งโดยนัยจากประเภทข้อมูล SQL ไปยังชนิดข้อมูล C # /. NET หากคุณต้องการแคสต์แบบโจ่งแจ้งอย่าใช้ฟังก์ชั่นนี้ให้ทำอย่างชัดเจนแทน
บังเหียน

17

ExecuteScalar จะกลับมา

  • null หากไม่มีการตั้งค่าผลลัพธ์
  • มิฉะนั้นคอลัมน์แรกของแถวแรกของชุดผลลัพธ์ซึ่งอาจเป็น DBNull

ถ้าคุณรู้ว่าคอลัมน์แรกของชุดผลลัพธ์เป็นสตริงดังนั้นเพื่อให้ครอบคลุมฐานทั้งหมดคุณต้องตรวจสอบทั้ง null และ DBNull สิ่งที่ต้องการ:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null) ? String.Empty : accountNumber.ToString();

โค้ดด้านบนขึ้นอยู่กับข้อเท็จจริงที่ว่า DBNull ToString ส่งคืนสตริงว่าง

หาก accountNumber เป็นประเภทอื่น (พูดว่าจำนวนเต็ม) คุณจะต้องมีความชัดเจนมากขึ้น:

object accountNumber = ...ExecuteScalar(...);
return (accountNumber == null || Convert.IsDBNull(accountNumber) ?     
         (int) accountNumber : 0;

หากคุณแน่ใจว่าชุดผลลัพธ์ของคุณจะมีอย่างน้อยหนึ่งแถวเสมอ (เช่น SELECT COUNT (*) ... ) คุณสามารถข้ามการตรวจสอบค่าว่างได้

ในกรณีของคุณข้อความแสดงข้อผิดพลาด "Unable to cast object of type" System.DBNull "to type" System.String` "ระบุว่าคอลัมน์แรกของชุดผลลัพธ์ของคุณเป็นค่า DBNUll นี่คือจากนักแสดงไปจนถึงสตริงในบรรทัดแรก:

string accountNumber = (string) ... ExecuteScalar(...);

ความคิดเห็นของ Marc_s ที่คุณไม่จำเป็นต้องตรวจสอบ DBNull ค่าผิด


ผลลัพธ์ของฉันจะไม่ส่งคืนแถวเสมอไป
Saif Khan

6

คุณสามารถใช้ตัวดำเนินการรวมค่าว่างของ C #

return accountNumber ?? string.Empty;

-1: ที่จะไม่รวบรวม: วิธีการส่งคืนสตริงและ accountNumber เป็นวัตถุ
โจ

2
ส่งคืน Cmd.ExecuteScalar (). ToString () ?? สตริงว่าง;
Chaitanya

return Cmd.ExecuteScalar () ToString () ทำงานให้ฉัน
Taran

3

มีอีกวิธีหนึ่งในการแก้ไขปัญหานี้ วิธีแก้ไขขั้นตอนการจัดเก็บของคุณ โดยใช้ฟังก์ชัน ISNULL (ฟิลด์ของคุณ "") sql คุณสามารถส่งคืนสตริงว่างได้หากค่าที่ส่งคืนเป็นค่าว่าง

จากนั้นคุณมีรหัสที่สะอาดเป็นเวอร์ชันดั้งเดิม


3

นี่เป็นวิธีการทั่วไปที่ฉันใช้เพื่อแปลงอ็อบเจ็กต์ใด ๆ ที่อาจเป็น DBNull.Value:

public static T ConvertDBNull<T>(object value, Func<object, T> conversionFunction)
{
    return conversionFunction(value == DBNull.Value ? null : value);
}

การใช้งาน:

var result = command.ExecuteScalar();

return result.ConvertDBNull(Convert.ToInt32);

สั้นกว่า:

return command
    .ExecuteScalar()
    .ConvertDBNull(Convert.ToInt32);

2

ฉันคิดว่าคุณสามารถทำได้ดังนี้:

string accountNumber = DBSqlHelperFactory.ExecuteScalar(...) as string;

หาก accountNumber เป็นโมฆะหมายความว่า DBNull ไม่ใช่สตริง :)


หรือreturn (accountNumber as string) ?? string.Empty;โดยที่ accountNumber ยังคงเป็นobjectไฟล์. หากคุณต้องการให้ฐานข้อมูลของคุณเรียกใช้สายของตัวเอง
Brian

1

String.Concat แปลงค่า DBNull และ null เป็นสตริงว่าง

public string GetCustomerNumber(Guid id)
{
   object accountNumber =  
          (object)DBSqlHelperFactory.ExecuteScalar(connectionStringSplendidCRM, 
                                CommandType.StoredProcedure, 
                                "spx_GetCustomerNumber", 
                                new SqlParameter("@id", id));

    return String.Concat(accountNumber);

 }

อย่างไรก็ตามฉันคิดว่าคุณสูญเสียบางอย่างในการเข้าใจโค้ด


1
จะเกิดอะไรขึ้นถ้าคุณเขียนreturn "" + accountNumber;?
Zev Spitz

0

เนื่องจากฉันมีอินสแตนซ์ที่ไม่เป็นโมฆะและถ้าฉันเทียบกับ DBNULL ฉันได้ Operator '==' cannot be applied to operands of type 'string' and 'system.dbnull' exeption และถ้าฉันพยายามเปลี่ยนเพื่อเปรียบเทียบกับ NULL มันก็ไม่ได้ผล (เนื่องจาก DBNull เป็นวัตถุ) แม้ว่าจะเป็นคำตอบที่ยอมรับก็ตาม

ฉันตัดสินใจที่จะใช้คำหลัก 'is' ดังนั้นผลลัพธ์จึงน่าอ่านมาก:

data = (item is DBNull) ? String.Empty : item


-1

ฉันใช้ส่วนขยายเพื่อขจัดปัญหานี้ให้ฉันซึ่งอาจเป็นหรือไม่เป็นอย่างที่คุณเป็น

มันจะเป็นแบบนี้:

public static class Extensions
{

    public String TrimString(this object item)
    {
        return String.Format("{0}", item).Trim();
    }

}

บันทึก:

ส่วนขยายนี้ไม่คืนnullค่า! ถ้าไอเท็มคือnullหรือDBNull.Valueมันจะส่งคืนสตริงว่าง

การใช้งาน:

public string GetCustomerNumber(Guid id)
{
    var obj = 
        DBSqlHelperFactory.ExecuteScalar(
            connectionStringSplendidmyApp, 
            CommandType.StoredProcedure, 
            "GetCustomerNumber", 
            new SqlParameter("@id", id)
        );
    return obj.TrimString();
}

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