Конференция ".Net" » ADO.NET: можно ли преобразовать DBNull ? [C#, WinXP]
 
  • Bronco © (16.08.05 16:50) [0]
    Ситуация следующая:
    Есть некий класс с определенным набором полей.
    Каждому полю присваиваю значения из DataTable
    например:
    DateOpen = Convert.ToDateTime(DT1.Rows[0]["DateOpen"]);


    где DateOpen - поле класса типа DateTime.
    Все хорошо до тех пор, пока в DataTable не натыкаюсь на пустое значение поля. Тут возникает exception, говорящий о том, что невозможно преобразовать DBNull в DateTime.
    Конечно есть вариант каждый раз проверять поле на наличие в нем пустого значения, напр. так:
    if (Convert.IsDBNull(DT1.Rows[0]["DateOpen"]))
    DateOpen = Convert.ToDateTime(null);
    else
    DateOpen = Convert.ToDateTime(DT1.Rows[0]["DateOpen"]);


    но как-то громоздко это, да и полей много, придется для каждого писать такую конструкцию.
    Может есть более простые способы преобразования DBNull в нужный тип?
    Спасибо заранее.
  • DiamondShark © (16.08.05 17:41) [1]

    > Может есть более простые способы преобразования DBNull в
    > нужный тип?

    Как только ты расскажешь, какое значение, скажем, Int32 соответсвует DBNull, я тебе тут же напишу способ преобразования.


    > Конечно есть вариант каждый раз проверять поле на наличие
    > в нем пустого значения, напр. так:
    > if (Convert.IsDBNull(DT1.Rows[0]["DateOpen"]))
    > DateOpen = Convert.ToDateTime(null);
    > else
    > DateOpen = Convert.ToDateTime(DT1.Rows[0]["DateOpen"]);

    Плохой вариант.
    Convert.ToXXX с аргументом null возвращает для value-типов заполненное нулями значение.
    И если дата 01-01-0001 (а это и есть "нулевая" дата) хотя и вполне валидное, но несколько экзотическое значение, то, скажем, нулевой Int32 или Double -- вообще ничем не примечательны.

    Никакого универсального (т.е. не зависящего от логики конкретного приложения) преобразования DBNull -> value-type нет и быть не может.
    По определению.
  • Bronco © (16.08.05 17:56) [2]

    > DiamondShark ©   (16.08.05 17:41) [1]


    > Как только ты расскажешь, какое значение, скажем, Int32
    > соответсвует DBNull, я тебе тут же напишу способ преобразования.

    Расскажу. См. класс TField в Delphi. Он почему-то "знает" как преобразовывать пустое (null) значение.


    > Плохой вариант.

    не понял, что в этом плохого? Ну нулевая дата, и что? По условию дата вполне может отсутствовать. А как по-другому инициализировать поле класса пустым значением?
  • Bronco © (16.08.05 18:04) [3]
    Дополнение: насчет TField могу ошибаться.
    Но либо он, либо движок соответствующей БД занимается этим преобразованием. И точно знает во что нужно преобразовать null в каждом конкретном случае.
  • DiamondShark © (16.08.05 18:49) [4]

    > Расскажу. См. класс TField в Delphi. Он почему-то "знает"
    > как преобразовывать пустое (null) значение.

    Фиги разные. Класс TField ничего не преобразует. Он имеет свойство Value типа Variant (для которого значение NULL входит в множество значений) и свойство IsNull.
    А методы доступа свойств AsXXX -- заглушки с исключениями.
    Преобразования вводят классы-наследники.


    > не понял, что в этом плохого? Ну нулевая дата, и что?

    А то, что нулевая дата и null -- это разные значения.
    А с целыми ты что будешь делать? Нулём заменять? Так ноль -- значение не лучше и не хуже любого другого.


    > Но либо он, либо движок соответствующей БД занимается этим
    > преобразованием. И точно знает во что нужно преобразовать
    > null в каждом конкретном случае.

    Ничем подобным никакой движок не занимается. Null -- он по-жизни Null.

    Тип данных -- это множество значений. Null -- это признак значения вне множества значений. Поэтому однозначно отображаться на множество значений не может.
    Только ты, как разработчик прикладной логики, можешь принять решение, на какое значение из множества значений типа тебе отображать Null.
  • Канадец (16.08.05 22:35) [5]
    >Bronco ©
    Эта проблема не одному тебе спать не даёт. Никаких преобразований структурных типов в null, естественно, нет и как здесь правильно заметили, быть не может. Microsoft обещает исправиться в .net framework 2.0 введением  Nullable Types.
  • Bronco © (17.08.05 11:05) [6]

    > DiamondShark ©   (16.08.05 18:49) [4]

    Демагогия чистой воды.
    А на вопрос ты все-таки мне ответь: чем инициализировать дату (поле класса), если она в базе null ? (только не надо говорить "чем угодно")

    Я все-таки повторюсь, что в TField(его наследниках) эта проблема была решена вполне приемлемо. В .Net судя по всему - нет. (Основной мой вопрос закрыт)
    Ты с пеной у рта можешь доказывать что "null это null и каждый для себя должен сам решать, во что его преобразовывать", а мое мнение, что это есть недоработка Microsoft, поскольку существует очевидное решение этой проблемы, которое годами использовалось (кстати тем же Microsoft-ом)


    > Канадец   (16.08.05 22:35) [5]

    Спасибо, я уже так и понял. Хотелось бы верить, что исправятся.
  • Игорь Шевченко © (17.08.05 11:52) [7]
    Bronco ©   (17.08.05 11:05) [6]


    > Я все-таки повторюсь, что в TField(его наследниках) эта
    > проблема была решена вполне приемлемо.


    И как она решена ?
  • DiamondShark © (17.08.05 12:33) [8]

    > Демагогия чистой воды.

    Убей себя об стену.


    > А на вопрос ты все-таки мне ответь: чем инициализировать
    > дату (поле класса), если она в базе null ? (только не надо
    > говорить "чем угодно")

    Ничем. Чем бы ты не инициализировал, всё равно это будет валидное значение типа. И, следовательно, встаёт проблема отличения одного валидного значения, которое подставлено вместо null, от другого такого же значения, но которое не подставлено вместо null.
    В одних случаях можно выделить из области значений такие, которые заведомо не используются в задаче, и использовать их. В других случаях нельзя.
    Но абсолютно приемлемого решения нет, кроме как расширить область значений, например, дополнив флагом IsNull.


    > Я все-таки повторюсь, что в TField(его наследниках) эта
    > проблема была решена вполне приемлемо.

    Ты туп. TField -- это структура, содержащая атомарное значение плюс флаг IsNull. А ты хочешь обойтись только атомарным значением.


    > В .Net судя по всему - нет.

    Используй вместо value-типов object.
    Тогда у тебя переменные будут содержать либо boxed-значение, либо null.


    > поскольку существует очевидное решение этой проблемы, которое
    > годами использовалось (кстати тем же Microsoft-ом)

    (*вкрадчивым голосом*)И какое же?
  • Bronco © (17.08.05 13:53) [9]

    > Игорь Шевченко ©   (17.08.05 11:52) [7]

    ну например, взгляни на код методов
    TStringField.GetAsString
    TIntegerField.GetAsInteger

    Если в наборе данных обнаруживается null (GetData вернула False), то возвращается наиболее близкое по смыслу значение для каждого из типов.
    Для string - пустая строка. Для integer - 0 и т.д. Чем не решение?
    Я конечно не хочу сказать, что такой подход претендует на истину, но для меня это было гораздо удобнее, чем самому отлавливать null-ы и преобразовывать их во что бы то ни было.


    > DiamondShark ©   (17.08.05 12:33) [8]

    Побереги нервы, дружище.
  • Суслик © (17.08.05 13:56) [10]

    >  [9] Bronco ©   (17.08.05 13:53)


    > > DiamondShark ©   (17.08.05 12:33) [8]
    >
    > Побереги нервы, дружище.


    А ты послушай, что он говорит :)
    Особенно про boxing
  • Bronco © (17.08.05 14:03) [11]
    Удалено модератором
  • Суслик © (17.08.05 14:13) [12]

    > [11] Bronco ©   (17.08.05 14:03)

    Я прав, а ты нет :)
    Он тебе очень дельно объяснил. И теорию реализации null, и практику (object + boxing).
    Читай.
  • Игорь Шевченко © (17.08.05 14:35) [13]
    Bronco ©   (17.08.05 13:53) [9]


    > ну например, взгляни на код методов
    > TStringField.GetAsString
    > TIntegerField.GetAsInteger


    Коран запрещает сделать подобную функциональность в своей программе ?


    > Если в наборе данных обнаруживается null (GetData вернула
    > False), то возвращается наиболее близкое по смыслу значение
    > для каждого из типов.


    Степень близости по смыслу можно узнать ? :)
  • Bronco © (17.08.05 14:43) [14]

    > Суслик ©   (17.08.05 14:13) [12]

    Прав тот у кого больше прав. А у нас с тобой их одинаково. Я неправ? :-)
    --
    Вобщем, с таким подходом пока тоже есть определенные непонятки.
    1) Слишком критична скорость работы программы. Постоянные обращения к полям класса. Как скажется время на распаковку объектов на скорости работы программы... пока неясно. Буду проверять.
    2) Опять же, при обращении к объекту, дабы получить значение мне придется сначала проверять его на null, т.е. лишнего if-a не избежать. Либо писать собственный метод. Или я чего не понимаю?
  • Bronco © (17.08.05 14:50) [15]

    > Игорь Шевченко ©   (17.08.05 14:35) [13]


    > Коран запрещает сделать подобную функциональность в своей
    > программе ?

    Нет конечно :-)


    > Степень близости по смыслу можно узнать ? :)

    Ну хорошо. Придрался-таки к словам :-) Термин "степень близости" видимо тут не подходит. Просто некие дяди постановили null в числовом поле преобразовывать в 0. Null в символьном поле преобразовывать в ''. И мне удобно и им, видимо, проблем меньше :-)
  • k2 © (17.08.05 14:57) [16]
    а в символьном итак null преобразуется в '',так што вам делов-то осталось всего ничего
  • DiamondShark © (17.08.05 14:59) [17]
    А вообще, как мне кажецца, вы фигнёй страдаете.

    Я вот в VS создал DataSet, она мне радостно сгенерировала классы-обертки для таблиц и строк.

    Смотрю:

    public long Field1 {
       get {
           try {
               return ((long)(this[this.tableTable1.Field1Column]));
               }

           catch (InvalidCastException e) {
               throw new StrongTypingException("Cannot get value because it is DBNull.", e);
           }

       }
       set {
            this[this.tableTable1.Field1Column] = value;
           }

       }


    Это, если кто не понял, кусок из автогенерированного наследника DataRow

    Теперь открываем схему данных, выделяем поле, и в свойство NullValue пишем, например, 123.
    Смотрим код:


    public long Field1 {
       get {
           if (this.IsField1Null()) {
               return 123;
           }

           else {
               return ((long)(this[this.tableTable1.Field1Column]));
           }

       }
       set {
           this[this.tableTable.Field1Column] = value;
       }

    }



    Короче, не надо про "недоработки Microsoft"...
  • DiamondShark © (17.08.05 15:04) [18]
    ЗЫ
    Есть ли такая тулзовина в Дельфи -- не знаю, бо пользуюсь вражеским продуктом.
  • Bronco © (17.08.05 15:13) [19]

    > DiamondShark ©   (17.08.05 14:59) [17]

    Дык я тоже про VS говорю. О Дельфи пока речи нет.
    Ну, в твоем коде, как я понял, используется типизированный DataSet, у меня немного другой случай. Есть DataTable без всяких DataSet-ов. И каждый раз в нем может оказаться неизвестно какая структура данных. Под каждый случай создавать типизированный набор данных... хотя может и стоит поменять подход.
  • DiamondShark © (17.08.05 15:39) [20]
    Да, типизированный.
    А как, извиняюсь, "некий класс с определенным набором полей" может взаимодействовать с "неизвестно какой структурой данных"?
  • Bronco © (17.08.05 16:12) [21]

    > DiamondShark ©   (17.08.05 15:39) [20]

    Классов несколько, каждый предназначен для своих целей.
    Все классы имеют потребность периодически получать некую инфу из базы. Т.е. каждый класс дает свой sql запрос. При этом, в рантайме создается DataTable и возвращается как результат.
  • Суслик © (18.08.05 11:27) [22]

    > [21] Bronco ©   (17.08.05 16:12)


    Если так, то пиши предка - суперкласс, который:
    1. Содержит метаинформацию о полях.
    2. Умеет читать таблицу.

    В общем, примерно как в дельфи сделано.
  • Bronco © (18.08.05 12:18) [23]

    > Суслик ©   (18.08.05 11:27) [22]

    По хорошему, наверное, надо было так и сделать.
    Но я пока решил проблему с DBNull другим путем.
    Отказался от использования класса Convert, и написал собственный (DBConvert), методы которого включают проверку на DBNull. По-моему так "дешевле".
  • Суслик © (18.08.05 12:21) [24]

    > По-моему так "дешевле".

    Это тебе решать.

    Решение о создании суперкласса сильно зависит от решаемой задачи и от ПОВТОРЯЕМОСТИ решения данной задачи в будущем.

    Меня раньше хлебом не корми, а дай написать инструментарий. Сейчас я долго буду думать и анализировать целесообразность данного действа.

    Так, что, скорее всего, так именно "дешевле".
  • Polevi © (18.08.05 12:38) [25]
    через отражение можно легко пишется универсальный код для любого типа
 
Конференция ".Net" » ADO.NET: можно ли преобразовать DBNull ? [C#, WinXP]
Есть новые Нет новых   [119220   +39][b:0][p:0.002]