2009-07-09 24 views
10

¿Cómo puedo asignar un objeto DataReader a un objeto de clase utilizando genéricos?C# - Asignación de IDataReader a Objetos utilizando genéricos

Por ejemplo tengo que hacer lo siguiente:

public class Mapper<T> 
    { 
     public static List<T> MapObject(IDataReader dr) 
     { 
      List<T> objects = new List<T>(); 

      while (dr.Read()) 
      { 
       //Mapping goes here... 
      } 

      return objects; 
     } 
    } 

Y después tengo que llamar a esta clase de método como el siguiente:

IDataReder dataReader = DBUtil.Fetchdata("SELECT * FROM Book"); 

List<Book> bookList = Mapper<Book>.MapObject(dataReder); 

foreach (Book b in bookList) 
{ 
    Console.WriteLine(b.ID + ", " + b.BookName); 
} 

Tenga en cuenta que, el Mapeador - clase debe ser capaz de mapear objetos de cualquier tipo representado por T.

+0

Una sugerencia: lea en un IEnumerable con un retorno de rendimiento. –

+0

// el mapeo va aquí, exactamente lo que te he mostrado en mi respuesta, puedes mapear cualquier objeto al lector de datos (más exactamente: inyectando valores de un IDataReader en un objeto CUALQUIER TIPO) – Omu

+0

¿Por qué no usarías un ¿ORM dedicado entonces? Un micro-ORM como Dapper parece ser una buena opción aquí. – nawfal

Respuesta

3

utilizo ValueInjecter para este

que estoy haciendo de esta manera:

while (dr.Read()) 
    { 
     var o = new User(); 
     o.InjectFrom<DataReaderInjection>(dr); 
     yield return o; 
    } 

que va a necesitar este ValueInjection para que esto funcione:

public class DataReaderInjection : KnownSourceValueInjection<IDataReader> 
    { 
     protected override void Inject(IDataReader source, object target, PropertyDescriptorCollection targetProps) 
     { 
      for (var i = 0; i < source.FieldCount; i++) 
      { 
       var activeTarget = targetProps.GetByName(source.GetName(i), true); 
       if (activeTarget == null) continue; 

       var value = source.GetValue(i); 
       if (value == DBNull.Value) continue; 

       activeTarget.SetValue(target, value); 
      } 
     } 
    } 
+0

¿Hay alguna biblioteca de inyecciones comunes y útiles en algún lugar? –

+0

@PavelHodek no hay una biblioteca, pero hay muchas en la solución de demostración principal de valueinjecter y también en las páginas Codeplex – Omu

+0

Pocas cosas. 1) Estás usando la reflexión para establecer el valor, esto es malo para el rendimiento. Ir a la ruta de expresión. 2) ¿Dónde está DataReaderInjection y KnownSourceValueInjection en su biblioteca? Creo que cambiaste los nombres desde esta respuesta? 3) ¿Está usando la reflexión detrás de escena cada vez en el lector de tiempo? ¿Lectura de bucle para obtener nombres de propiedad? Espero que no, pero no puedo convencerme a mí mismo sin ver la clase KnownSourceValueInjection. – nawfal

2

Bueno, no sé si cabe aquí, pero podría estar utilizando la palabra clave yield

public static IEnumerable<T> MapObject(IDataReader dr, Func<IDataReader, T> convertFunction) 
     { 
      while (dr.Read()) 
      { 
       yield return convertFunction(dr); 
      } 
     } 
+0

+1 Interesante uso de DI. Sería bueno que el tipo T también proporcione la implementación de convertFunction. : D –

+0

Este es un buen DI y se puede combinar con la solución de Omu. –

1

Esto va a ser muy difícil de hacer por la razón de que está básicamente tratando de asignar dos incógnitas juntos. En su objeto genérico, el tipo es desconocido, y en su lector de datos, la tabla es desconocida.

Así que lo que yo sugeriría es que cree algún tipo de atributo de columna para adjuntar a las propiedades de su entidad. Y luego revise los atributos de propiedad e intente buscar los datos de esos atributos en el lector de datos.

Su mayor problema va a ser, lo que sucede si una de las propiedades no se encuentra en el lector, o viceversa, una de las columnas en el lector no se encuentra en la entidad.

Buena suerte, pero si quieres hacer algo como esto, probablemente quieras un ORM o al menos algún tipo de implementación de Active Record.

+0

mira mi respuesta, no es tan difícil :), y no hay atributos requeridos – Omu

1

La manera más fácil que se me ocurre de improviso sería proporcionar un delegado Func<T,T> para convertir cada columna y construir su libro.

Alternativamente, si siguió algunas convenciones, podría manejar esto mediante la reflexión. Por ejemplo, si cada columna asignada a una propiedad en el objeto resultante con el mismo nombre, y usted restringió T en su Mapeador a proporcionar una T constructable, podría usar la reflexión para establecer el valor de cada propiedad al valor en la columna correspondiente .

1

lo siguiente acerca

abstract class DataMapper 
{ 
    abstract public object Map(IDataReader); 
} 

class BookMapper : DataMapper 
{ 
    override public object Map(IDataReader reader) 
    { 
     ///some mapping stuff 
     return book; 
    } 
} 

public class Mapper<T> 
{ 
    public static List<T> MapObject(IDataReader dr) 
    { 
     List<T> objects = new List<T>(); 
     DataMapper myMapper = getMapperFor(T); 
     while (dr.Read()) 
     { 
      objects.Add((T)myMapper(dr)); 
     } 

     return objects; 
    } 

    private DataMapper getMapperFor(T myType) 
    { 
     //switch case or if or whatever 
     ... 
     if(T is Book) return bookMapper; 

    } 
} 

No sabe si es sintácticamente correcta, pero espero que te dan la idea.

+0

Puedes evitar las condiciones if else y confiar en el polimorfismo siempre que la clase Book implemente Func . – nawfal

1

Yo recomendaría que tendrá que utilizar para este AutoMapper.

Cuestiones relacionadas