Проблема с обнуляемыми типами, DBNulls и строками данных

Я видел этот поток в Переполнении стека относительно преобразования между DBNull и обнуляемыми типами, но я все еще запутался. Я написал такой код с некоторым кодом для взлома, чтобы иметь дело с обнуляемыми типами для DateTime и int, которые я покажу ниже, но это беспорядок, и я хочу использовать Nullable типы:

 DataTable dt = ds.Tables[0];
 List<RON> list = (from dr in dt.AsEnumerable()
                              select new RON
                              {
                                  RONId = dr[0].ToString(),
                                  StaffNumber = dr[1].ToString(),
                                  CheckInDate = GetCheckInDate(dr[2]),
                                  NonMissionIndicator = dr[3].ToString(),
                                  Comments = dr[4].ToString(),
                                  NonComplyIndicator = dr[5].ToString(),
                                  LOAirport = dr[6].ToString(),
                                  RONAirport = dr[7].ToString(),
                                  PropertyId = GetPropertyId(dr[8]),
                                  PropertyChain = dr[9].ToString(),
                                  PropertyName = dr[10].ToString(),
                                  PropertyStreet = dr[11].ToString(),
                                  PropertyStreet2 = dr[12].ToString(),
                                  PropertyCity = dr[13].ToString(),
                                  PropertyState = dr[14].ToString(),
                                  PropertyPostalCode = dr[15].ToString(),
                                  PropertyPhone = dr[16].ToString(),
                                  FAX = dr[17].ToString(),
                                  PropertyCountry = dr[18].ToString(),
                                  PropertyLongitude = GetPropertyLongitude(dr[19]),
                                  PropertyLatitude = GetPropertyLatitude(dr[20]),
                                  PropertyAirport = dr[21].ToString(),
                                  ReportedBy = dr[22].ToString(),
                                  ReportedDTS= GetReportedByDate(dr[23]),
                                  CanceledBy = dr[24].ToString(),
                                  CanceledDTS = GetCanceledByDate(dr[25])
                              }).ToList();
            return list;
        }

        //TODO: Change Defaukt date
        private static DateTime GetCheckInDate(object checkInDate)
        {
            return checkInDate == DBNull.Value ? new DateTime(2000, 1, 1, 00, 00, 00) : Convert.ToDateTime(checkInDate);
        }

        //TODO: Change Defaukt date
        private static DateTime GetReportedByDate(object reportedByDate)
        {
            return reportedByDate == DBNull.Value ? new DateTime(2000, 1, 1, 00, 00, 00) : Convert.ToDateTime(reportedByDate);
        }

        //TODO: Change Defaukt date
        private static DateTime GetCanceledByDate(object canceledByDate)
        {
            return canceledByDate == DBNull.Value ? new DateTime(2000, 1, 1, 00, 00, 00) : Convert.ToDateTime(canceledByDate);
        }

        private static Int32 GetPropertyId(object propertyId)
        {
            return propertyId == DBNull.Value ? 0 : Convert.ToInt32(propertyId);
        }

        private static double GetPropertyLongitude(object propertyLongitude)
        {
            return propertyLongitude == DBNull.Value ? 0.0 : Convert.ToDouble(propertyLongitude);
        }

        private static double GetPropertyLatitude(object propertyLatitude)
        {
            return propertyLatitude == DBNull.Value ? 0.0 : Convert.ToDouble(propertyLatitude);
        }

RON теперь определяется как:

public class RON
    {
        public string RONId { get; set; }
        public string StaffNumber { get; set; }
        public DateTime CheckInDate { get; set; }
        public string NonMissionIndicator { get; set; }
        public string NonComplyIndicator { get; set; }
        public string LOAirport { get; set; }
        public string RONAirport { get; set; }
        public int PropertyId { get; set; }
        public string PropertyChain { get; set; }
        public string PropertyName { get; set; }
        public string PropertyStreet { get; set; }
        public string PropertyStreet2 { get; set; }
        public string PropertyCity { get; set; }
        public string PropertyState { get; set; }
        public string PropertyPostalCode { get; set; }
        public string PropertyCountry { get; set; }
        public string PropertyPhone { get; set; }
        public string FAX { get; set; }
        public double PropertyLongitude { get; set; }
        public double PropertyLatitude { get; set; }
        public string PropertyAirport { get; set; }
        public string ReportedBy { get; set; }
        public DateTime ReportedDTS { get; set; }
        public string CanceledBy { get; set; }
        public DateTime CanceledDTS { get; set; }
        public string Comments { get; set; }

Парень из базы данных говорит мне, что это возвращение из Oracle в DataSet/ курсор:

 RON_ID                                    NOT NULL VARCHAR2(40)
 STAFF_NUM                                 NOT NULL VARCHAR2(12)
 CHECKIN_DATE                              NOT NULL DATE
 NONMISSION_IND                            NOT NULL VARCHAR2(1)
 COMMENTS                                           VARCHAR2(4000)
 NONCOMPLY_IND                                      VARCHAR2(4000)
 PROPERTY_ID                               NOT NULL NUMBER(38)
 PROPERTY_CHAIN                                     VARCHAR2(2)
 PROPERTY_NAME                                      VARCHAR2(255)
 RON_AIRPORT                               NOT NULL VARCHAR2(3)
 PROPERTY_STREET                                    VARCHAR2(255)
 PROPERTY_STREET2                                   VARCHAR2(255)
 PROPERTY_CITY                                      VARCHAR2(255)
 PROPERTY_STATE                                     VARCHAR2(3)
 PROPERTY_POSTALCODE                                VARCHAR2(255)
 PROPERTY_PHONE                                     VARCHAR2(20)
 PROPERTY_FAX                                       VARCHAR2(20)
 PROPERTY_COUNTRY                                   VARCHAR2(2)
 PROPERTY_LONGITUDE                                 NUMBER
 PROPERTY_LATITUDE                                  NUMBER
 PROPERTY_AIRPORT                                   VARCHAR2(3)
 REPORTED_BY                                        VARCHAR2(50)
 REPORTED_DTS                              NOT NULL DATE
 CANCELED_BY                                        VARCHAR2(50)
 CANCELED_DTS                                       DATE

Как я могу объявить RON с Nullable Types и как я могу выполнять операции с Nullable Dates и тому подобное? Нужно ли проверять строки?

4 ответа

Решение

Чтобы определить тип значения (например, DateTime) как Nullable..., выполните одно из следующих действий:

DateTime? ReportedDTS

ИЛИ ЖЕ

Nullable<DateTime> ReportedDTS

Ссылочные типы (такие как строка) уже "обнуляются".

Чтобы получить значение Nullable из DataRow, вы можете использовать метод расширения поля:

DateTime? nullableDate = dataRow.Field<DateTime?>("ColumnName");

Это автоматически преобразует DBNull в ноль.

Так что для вашего примера вы можете сделать что-то вроде:

select new RON
           {
               RONId = dr.Field<string>("RON_ID"),
               // snip...
               CheckInDate = dr.Field<DateTime?>("CHECKIN_DATE"),
               // snip...
           };

Рассматривая исходный вопрос, я вижу, что при вызове.ToString() для нулевого значения возникает ошибка.

Это происходит, когда поле набора данных имеет значение DBNULL.
Лучший подход состоит в том, чтобы сначала проверить на null, прежде чем вызывать какие-либо методы объекта.
т.е.

DataTable dt = ds.Tables[0]; List<RON> list = (from dr in dt.AsEnumerable() select new RON { RONId = dr.IsNull("RONid") ? "0" : dr.Field<String>("RONid"), StaffNumber = dr.IsNull("StaffNumber") ? "0" : dr.Field<String>("StaffNumber"), ... etc, ... etc }).ToList(); return list; }

PS:

  • Проверьте ваш набор данных для совпадения значений полей. Здесь я предположил, что ваш набор данных "ColumnName" имеет то же имя, что и переменная, в которую он передается, но это не всегда так.
  • Совет, используйте "ColumnName" вместо индекса, так как индекс может меняться при изменении БД, сохраненного процесса или вашего запроса.

Возможно, вам придется сравнить с DBNull перед назначением DateTime или null

myNullableDate = (DBNull.Value == list.Field<object>("FOO"))
          ? null 
          : (DateTime?)list.Field<DateTime?>("FOO");

Так как могут быть сделаны только типы значений NullableВам не нужно будет делать ничего особенного для строк. Примитивные типы (и DateTime) можно объявить обнуляемым, добавив знак вопроса после имени типа, например int? NullableNumber { get; set; },

Другие вопросы по тегам