Как лучше всего обрабатывать null int / char из базы данных SQL?

У меня есть база данных с дополнительным профилем пользователя. В профиле у меня есть строки, символы (для M или F) и целые числа.

Я столкнулся с проблемой, когда я пытаюсь указать пол пользователя в свойстве моего объекта Profile, и приложение вылетает из-за того, что не знает, как обрабатывать возвращаемое нулевое значение.

Я пробовал привести данные к соответствующему типу

char sex = (char)dt.Rows[0]["Sex"];

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

object.sex = null;  
if(dt.Rows[0]["Sex"] != DBNull.Value)
      object.sex = (char)dt.Rows[0]["Sex"];
object.WorkExt = null;
if(dt.Rows[0]["WorkExt"] != DBNull.Value)
      object.WorkExt = (int)dt.Rows[0]["WorkExt"];

Есть способ сделать это проще или лучше? Или я на правильном пути?


person Matt R    schedule 25.11.2008    source источник


Ответы (6)


Ответ Ротарда (используйте Is<ColumnName>Null()) работает только для типизированных наборов данных.

Для нетипизированных наборов данных вы должны использовать один из шаблонов в следующем коде. Если этот код не является окончательным, дайте мне знать, и я отредактирую его, пока он не станет окончательным. Это чрезвычайно распространенный вопрос, на который действительно должен быть только один правильный ответ.

using System.
using System.Data;

class Program
{
    static void Main(string[] args)
    {
        DataTable dt = new DataTable();
        dt.Columns.Add("test", typeof (char));
        dt.Columns["test"].AllowDBNull = true;

        DataRow dr = dt.Rows.Add();
        char? test;

        try
        {
            test = (char?)dr["test"];
        }
        catch (InvalidCastException)
        {
            Console.WriteLine("Simply casting to a nullable type doesn't work.");
        }

        test  = dr.Field<char?>("test");
        if (test == null)
        {
            Console.WriteLine("The Field extension method in .NET 3.5 converts System.DBNull to null.");                
        }

        test = (dr["test"] is DBNull) ? null : (char?) dr["test"];
        if (test == null)
        {
            Console.WriteLine("Before .NET 3.5, you have to check the type of the column's value.");
        }

        test = (dr["test"] == DBNull.Value) ? null : (char?) dr["test"];
        if (test == null)
        {
            Console.WriteLine("Comparing the field's value to DBNull.Value is very marginally faster, but takes a bit more code.");
        }

        // now let's put the data back

        try
        {
            dr["test"] = test;
        }
        catch (ArgumentException)
        {
            Console.WriteLine("You can't set nullable columns to null.");
        }

        dr.SetField("test", test);
        if (dr["test"] is DBNull)
        {
            Console.WriteLine("Again, in .NET 3.5 extension methods make this relatively easy.");
        }

        dr["test"] = (object)test ?? DBNull.Value;
        if (dr["test"] is DBNull)
        {
            Console.WriteLine("Before .NET 3.5, you can use the null coalescing operator, but note the awful cast required.");
        }


        Console.ReadLine();
    }
}
person Robert Rossney    schedule 25.11.2008

Типы, допускающие значение NULL, были разработаны именно для этой цели! использовать как char? вместо '(char?)'

class Foo {
    char? sex;
}
Foo object;

object.sex = dt.Rows[0]["Sex"] as char?;
person Jimmy    schedule 25.11.2008
comment
К сожалению, типы, допускающие значение NULL, были разработаны не для этого, как вы убедитесь, если протестируете этот код. Если для DataColumn установлено значение AllowDBNull, нулевым значением в этом столбце является DBNull.Value, а не null. Ваш код генерирует исключение InvalidCastException. - person Robert Rossney; 26.11.2008
comment
с каких это пор "as" вызывает исключение InvalidCastException? - person Jimmy; 26.11.2008

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

person BQ.    schedule 25.11.2008
comment
Это немного другое, потому что это не перезаписывает существующие значения с помощью DBNull. - person Jimmy; 26.11.2008

как насчет:

    internal static T CastTo<T>(object value)
    {
        return value != DBNull.Value ? (T)value : default(T);
    }

а затем используйте его как:

        return new EquipmentDetails(
            CastTo<int>(reader["ID"]),
            CastTo<int>(reader["CategoryID"]),
            CastTo<string>(reader["Description"]));

и т.д...

person Community    schedule 26.11.2008

Является ли dt таблицей данных ADO.Net 2? Вы не можете сделать что-то вроде:

if(dt.Rows[0].IsSexNull()) {} else {}

? Кроме того, если у вас есть контроль над своей базой данных, не имеет ли смысла использовать бит, а не строку?

person gillonba    schedule 25.11.2008
comment
Есть сбитые с толку люди, которые действительно не знают, как ответить на этот вопрос, и другие, которые просто не любят вам говорить. Вы можете объединить эти группы в одну группу, но необходим как минимум третий вариант. - person Joel Coehoorn; 25.11.2008
comment
Очевидная проблема с использованием бита состоит в том, что даже если Пол является двоичным выбором, не очевидно, что означают Истина и Ложь. Кроме того, во многих системах это даже не тройной выбор: одна, которую я использую, поддерживает Male, Female, Unknown и Decline to State. И NULL, что не то же самое, что Unknown. - person Robert Rossney; 27.11.2008

Я бы сделал это примерно так же, как и ты. Я бы написал для него функцию:

Что-то, что делает:

object.sex = handle(dt.Rows[0]["Sex"]);

И в дескрипторе вы выполняете проверку == DBNull.Value.

person Burkhard    schedule 25.11.2008
comment
Это работает, только если ваша функция возвращает объект, а вы передаете возвращаемое значение в char? в задании. Методы поля в расширениях DataRow .NET 3.5 делают это за вас. - person Robert Rossney; 26.11.2008