Проблема с обнуляемыми типами, 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; }
,