Manejar DBNull en C#
datareader (13)
¿Hay una forma mejor / más limpia de hacer esto?
int stockvalue = 0;
if (!Convert.IsDBNull(reader["StockValue"]))
stockvalue = (int)reader["StockValue"];
Aquí hay una manera.
int stockvalue = Convert.IsDbNull(reader["StockValue"]) ? 0 : (int)reader["StockValue"];
También puedes usar TryParse
int stockvalue = 0
Int32.TryParse(reader["StockValue"].ToString(), out stockvalue);
Háganos saber qué camino funciona para usted
El más corto (en mi humilde opinión) es:
int stockvalue = (reader["StockValue"] as int?) ?? 0;
Explicación:
- Si el lector ["StockValue"] es de tipo int , se devolverá el valor y el "??" el operador devolverá el resultado
- Si el lector ["StockValue"] NO es de tipo int (por ejemplo, DBNull), se devolverá nulo y el "??" el operador devolverá el valor 0 (cero).
Escribí un método de extensión hace varios días. Al usarlo, podrías hacer:
int? stockvalue = reader.GetValue<int?>("StockValue");
Aquí está el método de extensión (modifíquelo para sus necesidades):
public static class ReaderHelper
{
public static bool IsNullableType(Type valueType)
{
return (valueType.IsGenericType &&
valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
}
public static T GetValue<T>(this IDataReader reader, string columnName)
{
object value = reader[columnName];
Type valueType = typeof(T);
if (value != DBNull.Value)
{
if (!IsNullableType(valueType))
{
return (T)Convert.ChangeType(value, valueType);
}
else
{
NullableConverter nc = new NullableConverter(valueType);
return (T)Convert.ChangeType(value, nc.UnderlyingType);
}
}
return default(T);
}
}
La forma en que manejo esto es
int? stockvalue = reader["StockValue"] as int?;
Muy simple, limpio y una línea. Si por alguna razón no puedo tener un valor nulo (que normalmente no me gusta, ya que preferiría saber si un valor tiene un significado o si fue unitario para un tipo primitivo) lo haría:
int stockvalue = (reader["StockValue"] as int?).GetValueOrDefault(-1);
Puede hacer esta conversión directamente en su consulta DB, evitando así el caso especial.
Pero yo no llamaría a eso ''limpiador'', a menos que pueda usar ese formulario de manera consistente en su código, ya que perdería información al devolver ''0'' en lugar de NULL desde el DB.
Realmente no. Podrías encapsularlo en un método:
public int getDBIntValue(object value, int defaultValue) {
if (!Convert.IsDBNull(value)) {
return (int)value;
}
else {
return defaultValue;
}
Y llámalo así:
stockVaue = getDBIntVaue(reader["StockValue"], 0);
O puede usar coalesce
en su consulta para forzar que el valor devuelto sea no nulo.
Editar - errores de código tonto corregidos en función de los comentarios recibidos.
Sí, puedes usar int?
De esta forma, puede tener un valor predeterminado de nulo en lugar de 0. Como el resultado de stockvalue podría ser 0, no hay confusión sobre si la base de datos era 0 o nula. Por ejemplo, como este (pre nullable) tuvimos una inicialización predeterminada de -1 para representar que no se asignó ningún valor. Personalmente, pensé que esto era un poco peligroso porque si olvidas configurarlo en -1, hay un problema de corrupción de datos que puede ser realmente difícil de rastrear.
http://msdn.microsoft.com/en-us/library/2cf62fcy(VS.80).aspx
int? stockvalue = null;
if (!Convert.IsDBNull(reader["StockValue"]))
stockvalue = (int)reader["StockValue"];
//Then you can check
if(stockValue.HasValue)
{
// do something here.
}
Si bien es conveniente hacer referencia al reader["StockValue"]
, no es muy eficiente. Tampoco está fuertemente tipado, ya que devuelve un tipo de object
.
En cambio, dentro de tu código, haz algo como esto:
int stockValueOrdinal = reader.GetOrdinal("StockValue");
int? stockValue = reader.IsDbNull(stockValueOrdinal) ?
null :
reader.GetInt32(stockValueOrdinal);
Por supuesto, es mejor obtener todos los ordinales al mismo tiempo, luego usarlos en todo el código.
Tengo dos siguientes métodos de extensión en mi proyecto:
public static T GetValueSafe<T>(this IDataReader dataReader, string columnName, Func<IDataReader, int, T> valueExtractor)
where T : class
{
T value;
if (dataReader.TryGetValueSafe(columnName, valueExtractor, out value))
{
return value;
}
return null;
}
public static bool TryGetValueSafe<T>(this IDataReader dataReader, string columnName, Func<IDataReader, int, T> valueExtractor, out T value)
{
int ordinal = dataReader.GetOrdinal(columnName);
if (!dataReader.IsDBNull(ordinal))
{
// Get value.
value = valueExtractor.Invoke(dataReader, ordinal);
return true;
}
value = default(T);
return false;
}
El uso puede ser así:
string companyName = dataReader.GetValueSafe("CompanyName", (reader, ordinal) => reader.GetString(ordinal));
use el tipo Nullable<int>
... int?
para abreviar
int stockvalue = reader["StockValue"] != DbNull.Value ? Convert.ToInt32(reader["StockValue"]) : 0;
int? stockValue = reader["StockValue"] == null || reader["StockValue"] == DBNull.Value ? null : (int?)reader["StockValue"];
int? stockvalue = (int?)(!Convert.IsDBNull(result) ? result : null);
Una posible solución para que se asegure de que DBNull se transmita a su código. Para nuestro grupo, como una mejor práctica, tratamos de no permitir columnas NULL en la base de datos a menos que realmente se necesite. Hay más sobrecarga en la codificación para manejarlo, y algunas veces solo repensar el problema hace que no sea necesario.