SQL Veri Okuyucu - Boş sütun değerlerini işleme


298

Bir veritabanından POCOs oluşturmak için bir SQLdatareader kullanıyorum. Kod, veritabanında boş bir değerle karşılaşma durumu dışında çalışır. Örneğin, veritabanındaki FirstName sütunu bir null değer içeriyorsa, bir istisna atılır.

employee.FirstName = sqlreader.GetString(indexFirstName);

Bu durumda null değerleri ele almanın en iyi yolu nedir?

Yanıtlar:


471

Şunları kontrol etmeniz gerekir IsDBNull:

if(!SqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

Bu durumu tespit etmenin ve halletmenin tek güvenilir yolu budur.

Bu şeyleri uzantı yöntemlerine sarılmış ve sütun gerçekten ise varsayılan bir değer döndürme eğilimindedir null:

public static string SafeGetString(this SqlDataReader reader, int colIndex)
{
   if(!reader.IsDBNull(colIndex))
       return reader.GetString(colIndex);
   return string.Empty;
}

Şimdi şöyle diyebilirsiniz:

employee.FirstName = SqlReader.SafeGetString(indexFirstName);

ve bir daha asla bir istisna veya nulldeğer hakkında endişelenmenize gerek kalmayacak .


65
Birisi dizin yerine sütun adına ihtiyaç duyarsa , şunları yapabilirsiniz: int colIndex = reader.GetOrdinal(fieldname);ve @ marc_s SafeGetStringişlevini kolayca aşırı yükleyebilirsiniz .
ilans

2019'da burada olduğuma inanamıyorum, daha az
VB'de

Ayrıca şu şekilde yapılabilir: int ordinal = reader.GetOrdinal ("col_name"); uint? val = reader.IsDBNull (sıralı)? (uint?) null: okuyucu.GetUInt32 (sıralı);
ed22

Merhaba beyler! Kopyalamaya çalıştım ve forma yapıştırdım ve bir hata ile döndüm. Msgstr "Genişletme yöntemi, genel olmayan bir statik sınıfta tanımlanmalıdır."
Jansen Malaggay

SafeGetString içinde Reader.GetOrindal kullanıyorsanız ve bir döngü içinde SafeGetString kullanmak istiyorsanız, performansa çarpmadan bu nasıl başarılabilir?
AspUser7724

223

Varsayılan değerler asiçin ??operatörü operatörle birlikte kullanmalısınız . Değer türlerinin boş değer olarak okunması ve varsayılan olarak verilmesi gerekir.

employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);

asOperatör DBNull için onay dahil casting işler.


6
Birisi Yaş sütununu bir SQL bigint (int #) olarak değiştirirse, kodunuz 0 döndürerek sessizce başarısız olur. ZXX'ten gelen yanıt daha güvenilir IMO'dur.
Martin Ørding-Thomsen

Acaba 0 yerine -1 olarak varsayılan (int) geçersiz kılabilir miyim
Chris

5
@Chris - Varsayılan (int) değerini -1 ile değiştirebilmeniz gerekir.
stevehipwell

@ Stevo3000 Doğru! Bunu denedim ve yayınladıktan hemen sonra söylediğin gibi çalıştı, ama bu sayfaya geri dönmeyi unuttum :)
Chris

5
Burada "as" kullanmanın dizin hatalarını gizleyebileceğini unutmayın. Yanlışlıkla kullanırsanız sqlreader[indexAge] as string ?? "", her zaman alırsınız "". (int?)sqlreader[indexAge] ?? defaultValueBunun yerine gerçekten isteyip istemediğinizi düşünün , bu nedenle SQL'iniz değişirse kötü değerler yerine istisnalar alırsınız. @ Stevo3000: varsayılan (int) -1 değil, 0'dır. @Chris: Gerçekten istediğinizi kullandığınızdan emin olun.
me22

30

Bir dize için, nesne sürümünü (dizi operatörü kullanılarak erişilir) yayınlayabilir ve null'lar için boş bir dize atabilirsiniz:

employee.FirstName = (string)sqlreader[indexFirstName];

veya

employee.FirstName = sqlreader[indexFirstName] as string;

Tamsayılar için, boş bir int'e yayın yaparsanız, GetValueOrDefault () yöntemini kullanabilirsiniz.

employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault();

veya null birleştirme operatörü ( ??).

employee.Age = (sqlreader[indexAge] as int?) ?? 0;

2
İlk örneğinizde olduğu gibi açık döküm işe yaramıyor. Aynı hatayı atar
musefan

@musefan: Alanınız aslında bir dize mi? Değilse farklı bir hata alırsınız. Bu işe yarar ve bunlar örnek 1 ve 2 (sözdizimi dışında) arasında gerçek bir fark değildir.
Gitti Kodlama

1
@GoneCoding: Evet, boş bir dizedir ve ikincisi çalışırken kesinlikle birincisinin soruna neden olduğu bir durumdur. Sorunun null değerlerin nasıl ele alındığından kaynaklandığını düşünüyorum. nullİçinde olduğu gibi, bunlar bir DBNull nesnesi değil. İki ifade arasındaki fark, bir dize değilse ilk başarısız olur, ikincisi ise dize değilse null değerini döndürür.
musefan

23

IsDbNull(int)gibi yöntemleri kullanmaktan GetSqlDateTimeve daha sonra karşılaştırmaktan çok daha yavaştır DBNull.Value. İçin şu uzantı yöntemlerini deneyin SqlDataReader.

public static T Def<T>(this SqlDataReader r, int ord)
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return default(T);
    return ((INullable)t).IsNull ? default(T) : (T)t;
}

public static T? Val<T>(this SqlDataReader r, int ord) where T:struct
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? (T?)null : (T)t;
}

public static T Ref<T>(this SqlDataReader r, int ord) where T : class
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? null : (T)t;
}

Bunları şu şekilde kullanın:

var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);

5
System.Data.SqlTypes türleri açık operatörleri bu kodu kullanmaya çalışırken her yerde hata atıyor buluyorum ...
Tetsujin no Oni

Bunun neden başarısız olabileceğinin açıklaması için stackoverflow.com/a/21024873/1508467 adresine bakın (bir SQL int sütunu okumak için Val <int> kullanmayı deneyin).
Rhys Jones

12

Bunu yapmanın bir yolu, db boş değerlerini kontrol etmektir:

employee.FirstName = (sqlreader.IsDBNull(indexFirstName) 
    ? ""
    : sqlreader.GetString(indexFirstName));

12

reader.IsDbNull(ColumnIndex) birçok cevabın söylediği gibi çalışıyor.

Ve sütun isimleri ile çalışıyorsanız, sadece türleri karşılaştırmak daha rahat olabilir.

if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }

Bu aynı zamanda System.Data ve .NET
FW'nin

11

Satır sütun adını kullanarak bir veri okuyucusu içinde döndürüldüğünde, bir NULL sütun değeri olduğunu sanmıyorum .

Bunu yaparsanız datareader["columnName"].ToString();, her zaman boş bir dize olabilecek bir değer verecektir ( String.Emptykarşılaştırmanız gerekiyorsa).

Aşağıdakileri kullanırdım ve çok fazla endişelenmezdim:

employee.FirstName = sqlreader["columnNameForFirstName"].ToString();


11

Bu Çözüm satıcıya daha az bağımlıdır ve SQL, OleDB ve MySQL Reader ile çalışır:

public static string GetStringSafe(this IDataReader reader, int colIndex)
{
    return GetStringSafe(reader, colIndex, string.Empty);
}

public static string GetStringSafe(this IDataReader reader, int colIndex, string defaultValue)
{
    if (!reader.IsDBNull(colIndex))
        return reader.GetString(colIndex);
    else
        return defaultValue;
}

public static string GetStringSafe(this IDataReader reader, string indexName)
{
    return GetStringSafe(reader, reader.GetOrdinal(indexName));
}

public static string GetStringSafe(this IDataReader reader, string indexName, string defaultValue)
{
    return GetStringSafe(reader, reader.GetOrdinal(indexName), defaultValue);
}

1
Bu kodu şu anda doğrudan bir uzantı sınıfına kopyalayıp özelleştirme.
qxotk

8

Ne yapmak eğilimindedir, SELECT deyimindeki boş değerleri uygun bir şeyle değiştirin.

SELECT ISNULL(firstname, '') FROM people

Burada her boş değeri boş bir dize ile değiştiriyorum. Bu durumda kodunuz hata vermeyecektir.


Mümkünse, null'lardan kaçınmak için bunu kullanın. Aksi takdirde, Sonny Boy'un yardımcı yöntemlerin cevabını seviyorum.
Para İadesi Yok İade Yok

3
Neden statik, ayrı bir yardımcı yöntem? SqlDataReader üzerinde bir uzantı yöntemi daha zorlayıcı ve daha sezgisel görünmüyor ??
marc_s

7

Null değerini denetlemek için Genel bir işlev yazabilir ve NULL olduğunda varsayılan değeri dahil edebilirsiniz. Datareader'ı okurken buna çağrı yapın

public T CheckNull<T>(object obj)
        {
            return (obj == DBNull.Value ? default(T) : (T)obj);
        }

Datareader kullanımını okurken

                        while (dr.Read())
                        {
                            tblBPN_InTrRecon Bpn = new tblBPN_InTrRecon();
                            Bpn.BPN_Date = CheckNull<DateTime?>(dr["BPN_Date"]);
                            Bpn.Cust_Backorder_Qty = CheckNull<int?>(dr["Cust_Backorder_Qty"]);
                            Bpn.Cust_Min = CheckNull<int?>(dr["Cust_Min"]);
                         }

6

sqlreader.IsDBNull(indexFirstName)Okumayı denemeden önce kontrol edin .


5

Getpsyched'in cevabından etkilenerek, sütun değerini adına göre kontrol eden genel bir yöntem oluşturdum

public static T SafeGet<T>(this System.Data.SqlClient.SqlDataReader reader, string nameOfColumn)
{
  var indexOfColumn = reader.GetOrdinal(nameOfColumn);
  return reader.IsDBNull(indexOfColumn) ? default(T) : reader.GetFieldValue<T>(indexOfColumn);
}

Kullanımı:

var myVariable = SafeGet<string>(reader, "NameOfColumn")

Kim olduğunu bilmiyorum, ama seni kutsasın. Bu işlev üç saatten fazla çalışma tasarrufu sağladı.
Ramneek Singh

4

yardımcı yöntemler oluşturma hakkında

Dize için

private static string MyStringConverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return "";

        return o.ToString();
    }

kullanım

MyStringConverter(read["indexStringValue"])

Int için

 private static int MyIntonverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return 0;

        return Convert.ToInt32(o);
    }

kullanım

MyIntonverter(read["indexIntValue"])

Tarih için

private static DateTime? MyDateConverter(object o)
    {
        return (o == DBNull.Value || o == null) ? (DateTime?)null : Convert.ToDateTime(o);
    }

kullanım

MyDateConverter(read["indexDateValue"])

Not: DateTime için varialbe bildirimi

DateTime? variable;

4

Marc_s tarafından verilen cevaba ek olarak, SqlDataReader'dan değer almak için daha genel bir uzantı yöntemi kullanabilirsiniz:

public static T SafeGet<T>(this SqlDataReader reader, int col)
    {
        return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
    }

Bu yöntem "SafeGet" olarak adlandırılmaz çünkü T bir yapı ise, null'ları boş olmayan T için varsayılana dönüştürür - gerçekten güvenli değil. Belki de "GetValueOrDefault" olabilir.
Rhys Jones

@RhysJones Bu durumda neden bir yapı olarak T'ye sahip oluyorsunuz? Bunu yapsanız bile, yapıdaki null olmayan varsayılan değerin beklenen davranış olduğunu iddia ediyorum.
getpsyched

@RhysJones Ancak bu yöntem güvenli olmayabilir, SqlDataReader InvalidCastException gibi özel durumları işlemek gerekir kabul ediyorum.
getpsyched


2

Tüm değerleri veri okuyucularımızdan çıkarmak için bir dizi statik yöntem kullanıyoruz. Yani bu durumda bizDBUtils.GetString(sqlreader(indexFirstName)) , statik / paylaşılan yöntemler oluşturmanın yararı, aynı kontrolleri tekrar tekrar yapmak zorunda kalmamanızdır ...

Statik yöntem (ler), null değerlerini denetlemek için kod içerir (bu sayfadaki diğer yanıtlara bakın).


2

Koşullu işleci kullanabilirsiniz:

employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";

Aşağıdaki cevaplardan biriyle aynı, ancak 8 yıl geride!
beercohol

2

Burada yararlı bilgiler (ve bazı yanlış bilgiler) yayılmış bir sürü cevap var, hepsini bir araya getirmek istiyorum.

Sorunun kısa cevabı DBNull'u kontrol etmektir - neredeyse herkes bu bit üzerinde hemfikirdir :)

SQL veri türü başına null değerlerin okunması için bir yardımcı yöntem kullanmak yerine, genel bir yöntem bunu çok daha az kodla ele almamızı sağlar. Ancak, hem null değer türleri hem de başvuru türleri için tek bir genel yönteme sahip olamazsınız, bu Nullable türünde olası bir genel parametre olarak ayrıntılı olarak tartışılıyor mu? ve boş bırakılabilir her şey için C # genel tür kısıtı .

Bu nedenle, @ZXX ve @getpsyched'in cevaplarından sonra, null değerleri elde etmek için 2 yöntemle sonuçlandırdık ve null olmayan değerler için bir 3. ekledim (yöntemi adlandırma temelinde seti tamamlar).

public static T? GetNullableValueType<T>(this SqlDataReader sqlDataReader, string columnName) where T : struct
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? (T?)null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNullableReferenceType<T>(this SqlDataReader sqlDataReader, string columnName) where T : class
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNonNullValue<T>(this SqlDataReader sqlDataReader, string columnName)
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

Ben genellikle sütun isimleri, sütun dizinleri kullanıyorsanız bunları değiştirmek. Bu yöntem adlarına dayanarak, verilerin uzun süre önce yazılan koda bakarken null değer olup olmadığını beklediğimi söyleyebilirim.

İpuçları;

  • Veritabanında null olabilecek sütunların olmaması bu sorunu önler. Veritabanı üzerinde denetiminiz varsa, sütunlar varsayılan olarak boş olmamalı ve yalnızca gerektiğinde boş bırakılmalıdır.
  • C # 'as' operatörü ile veritabanı değerlerini yayınlamayın çünkü döküm yanlışsa sessizce null değerini döndürür.
  • Bir kullanarak varsayılan değer ifade int gibi değer türleri için boş olmayan değerlere veritabanı boş değerlere değişecek, tarih saat, bit vs.

Son olarak, yukarıdaki yöntemleri keşfettiğim tüm SQL Server veri türlerinde test ederken, bir SqlDataReader'dan doğrudan bir char [] alamazsınız, eğer bir char [] istiyorsanız bir dize almanız ve ToCharArray () kullanmanız gerekir.


1

Bir datatable için okunan bir Excel sayfasındaki boş hücreleri işlemek için aşağıda listelenen kodu kullanıyorum.

if (!reader.IsDBNull(2))
{
   row["Oracle"] = (string)reader[2];
}

1
private static void Render(IList<ListData> list, IDataReader reader)
        {
            while (reader.Read())
            {

                listData.DownUrl = (reader.GetSchemaTable().Columns["DownUrl"] != null) ? Convert.ToString(reader["DownUrl"]) : null;
                //没有这一列时,让其等于null
                list.Add(listData);
            }
            reader.Close();
        }

1

ve / veya atama ile üçlü operatör kullanın:

employee.FirstName = rdr.IsDBNull(indexFirstName))? 
                     String.Empty: rdr.GetString(indexFirstName);

varsayılan (null olduğunda) değeri her özellik türü için uygun şekilde değiştirin ...


1

Bu yöntem, sıfır tabanlı sütun sıralaması olması gereken indexFirstName öğesine bağımlıdır.

if(!sqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

Sütun dizinini bilmiyor ancak bir adı kontrol etmek istemiyorsanız bunun yerine bu uzantı yöntemini kullanabilirsiniz:

public static class DataRecordExtensions
{
    public static bool HasColumn(this IDataRecord dr, string columnName)
    {
        for (int i=0; i < dr.FieldCount; i++)
        {
            if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
                return true;
        }
        return false;
    }
}

Ve böyle bir yöntem kullanın:

if(sqlReader.HasColumn("FirstName"))
{
  employee.FirstName = sqlreader["FirstName"];
}

1

Eski bir soru ama belki birisinin hala bir cevaba ihtiyacı var

gerçekte bu konuda böyle çalıştım

İnt için:

public static object GatDataInt(string Query, string Column)
    {
        SqlConnection DBConn = new SqlConnection(ConnectionString);
        if (DBConn.State == ConnectionState.Closed)
            DBConn.Open();
        SqlCommand CMD = new SqlCommand(Query, DBConn);
        SqlDataReader RDR = CMD.ExecuteReader();
        if (RDR.Read())
        {
            var Result = RDR[Column];
            RDR.Close();
            DBConn.Close();
            return Result;
        }
        return 0;
    }

dize için aynı "0 yerine" "döndürür," "boş dizedir

böylece onu kullanabilirsiniz

int TotalPoints = GatDataInt(QueryToGetTotalPoints, TotalPointColumn) as int?;

ve

string Email = GatDatastring(QueryToGetEmail, EmailColumn) as string;

çok esnek, böylece herhangi bir sütunu okumak için herhangi bir sorgu ekleyebilirsiniz ve asla hata ile geri dönmeyecektir


0

@Marc_s yanıtına dayalı olarak başkalarının ihtiyaç duyduklarında kullanabileceği yardımcı sınıf:

public static class SQLDataReaderExtensions
    {
        public static int SafeGetInt(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? 0 : dataReader.GetInt32(fieldIndex);
        }

        public static int? SafeGetNullableInt(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.GetValue(fieldIndex) as int?;
        }

        public static string SafeGetString(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? string.Empty : dataReader.GetString(fieldIndex);
        }

        public static DateTime? SafeGetNullableDateTime(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.GetValue(fieldIndex) as DateTime?;
        }

        public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName)
        {
            return SafeGetBoolean(dataReader, fieldName, false);
        }

        public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName, bool defaultValue)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? defaultValue : dataReader.GetBoolean(fieldIndex);
        }
    }

0

Kulpları DbNull'u mantıklı bir şekilde dönüştürün.

employee.FirstName = Convert.ToString(sqlreader.GetValue(indexFirstName));

DBNull öğesinin boş bir dizeye dönüştürüldüğünü, boş bir değere dönüştürüldüğünü unutmayın.
Rhys Jones

-2

bunu da kontrol edebilirsin

if(null !=x && x.HasRows)
{ ....}

-1 Mesele bu değil: boş veya boş değer değil, boş sütun değeri SqlDataReader
mavimsi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.