while recorrer read data c# casting sqldatareader

recorrer - ¿Cómo(eficientemente) convertir(emitir?) Un campo SqlDataReader a su correspondiente tipo de c#?



read data sqldatareader c# (5)

Primero, permítanme explicar la situación actual: estoy leyendo registros de una base de datos y poniéndolos en un objeto para su uso posterior; hoy surgió una pregunta sobre el tipo de base de datos para la conversión de tipo C # (¿casting?).

Veamos un ejemplo:

namespace Test { using System; using System.Data; using System.Data.SqlClient; public enum MyEnum { FirstValue = 1, SecondValue = 2 } public class MyObject { private String field_a; private Byte field_b; private MyEnum field_c; public MyObject(Int32 object_id) { using (SqlConnection connection = new SqlConnection("connection_string")) { connection.Open(); using (SqlCommand command = connection.CreateCommand()) { command.CommandText = "sql_query"; using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow)) { reader.Read(); this.field_a = reader["field_a"]; this.field_b = reader["field_b"]; this.field_c = reader["field_c"]; } } } } } }

Esto (obviamente) está fallando porque los tres this.field_x = reader["field_x"]; las llamadas están lanzando el Cannot implicitly convert type ''object'' to ''xxx''. An explicit conversion exists (are you missing a cast?). Cannot implicitly convert type ''object'' to ''xxx''. An explicit conversion exists (are you missing a cast?). error del compilador

Para corregir esto, actualmente sé de dos maneras (usemos el ejemplo field_b ): el número uno es this.field_b = (Byte) reader["field_b"]; y el número dos es this.field_b = Convert.ToByte(reader["field_b"]); .

El problema con la opción número uno es que los campos DBNull arrojan excepciones ya que el molde está fallando (incluso con tipos anulables como String ), y el problema con el número dos es que no preserva valores nulos (el Convert.ToString(DBNull) produce un String.Empty ), y no puedo usarlos con enumeraciones también.

Entonces, después de un par de búsquedas en internet y aquí en StackOverflow, lo que se me ocurre es:

public static class Utilities { public static T FromDatabase<T>(Object value) where T: IConvertible { if (typeof(T).IsEnum == false) { if (value == null || Convert.IsDBNull(value) == true) { return default(T); } else { return (T) Convert.ChangeType(value, typeof(T)); } } else { if (Enum.IsDefined(typeof(T), value) == false) { throw new ArgumentOutOfRangeException(); } return (T) Enum.ToObject(typeof(T), value); } } }

De esta forma debería manejar cada caso.

La pregunta es: ¿Me estoy perdiendo algo? ¿Estoy haciendo un WOMBAT ( Waste of Money, Brain And Time ) ya que hay una forma más rápida y limpia de hacerlo? Está todo correcto? ¿Lucro?


¿no quieres usar el reader.Get* métodos ? Lo único molesto es que toman los números de columna, por lo que debe envolver el descriptor de acceso en una llamada a GetOrdinal ()

using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow)) { reader.Read(); this.field_a = reader.GetString(reader.GetOrdinal("field_a")); this.field_a = reader.GetDouble(reader.GetOrdinal("field_b")); //etc }


Así es como lo he tratado en el pasado:

public Nullable<T> GetNullableField<T>(this SqlDataReader reader, Int32 ordinal) where T : struct { var item = reader[ordinal]; if (item == null) { return null; } if (item == DBNull.Value) { return null; } try { return (T)item; } catch (InvalidCastException ice) { throw new InvalidCastException("Data type of Database field does not match the IndexEntry type.", ice); } }

Uso:

int? myInt = reader.GetNullableField<int>(reader.GetOrdinal("myIntField"));


El código genérico de Hanlding publicado aquí es genial, pero dado que el título de la pregunta incluye la palabra "eficientemente" publicaré mi respuesta menos genérica pero (espero) más eficiente.

Sugiero que uses los métodos getXXX que otros han mencionado. Para tratar el problema del número de columna del que habla el bebop, uso una enumeración como esta:

enum ReaderFields { Id, Name, PhoneNumber, ... } int id = sqlDataReader.getInt32((int)readerFields.Id)

Es un poco más de tipeo, pero luego no necesita llamar a GetOrdinal para encontrar el índice para cada columna. Y, en lugar de preocuparte por los nombres de las columnas, te preocupan las posiciones de las columnas.

Para tratar con columnas que aceptan nulos, debe verificar DBNull y quizás proporcionar un valor predeterminado:

string phoneNumber; if (Convert.IsDBNull(sqlDataReader[(int)readerFields.PhoneNumber]) { phoneNumber = string.Empty; } else { phoneNumber = sqlDataReader.getString((int)readerFields.PhoneNumber); }


Puede hacer un conjunto de métodos de extensión, un par por tipo de datos:

public static int? GetNullableInt32(this IDataRecord dr, string fieldName) { return GetNullableInt32(dr, dr.GetOrdinal(fieldName)); } public static int? GetNullableInt32(this IDataRecord dr, int ordinal) { return dr.IsDBNull(ordinal) ? null : (int?)dr.GetInt32(ordinal); }

Esto se torna un poco tedioso de implementar, pero es bastante eficiente. En System.Data.DataSetExtensions.dll, Microsoft resolvió el mismo problema para los DataSets con un método Field<T> , que maneja genéricamente múltiples tipos de datos y puede convertir DBNull en Nullable.

Como experimento, una vez implementé un método equivalente para DataReaders, pero terminé usando Reflector para tomar prestada una clase interna de DataSetExtensions (UnboxT) para hacer las conversiones de tipo reales de manera eficiente. No estoy seguro de la legalidad de distribuir esa clase prestada, por lo que probablemente no debería compartir el código, pero es bastante fácil buscarse a sí mismo.


Si un campo permite valores nulos, no use tipos primitivos regulares. Use el tipo nullable C # y la palabra clave as .

int? field_a = reader["field_a"] as int?; string field_b = reader["field_a"] as string;

Agregar un ? a cualquier tipo de C # no anulable lo hace "nulo". El uso de la palabra clave as intentará enviar un objeto al tipo especificado. Si el molde falla (como lo haría si el tipo es DBNull ), el operador devuelve null .

Nota: Otro beneficio pequeño de usar es que es un poco más rápido que el lanzamiento normal. Dado que también puede tener algunas desventajas, como hacer que sea más difícil rastrear errores si intentas usar el tipo incorrecto, esto no se debe considerar una razón para usar siempre as sobre el casting tradicional. El casting regular ya es una operación bastante barata.