2011-01-04 13 views
5

Tengo un elemento de clase que representa un elemento en una lista. Tengo en él una función que llama a un procedimiento almacenado que devuelve datatable y necesito convertir la datatable en una matriz de elementos. Aquí es lo que hago:traer datarow al objeto C#

public class Item 
{ 
    private string _ItemIdDataName = "item_id"; 
    private string _ItemNameDataName = "item_name"; 
    private string _PriceDataName = "price"; 

    public long ItemId { get; set; } 
    public string ItemName { get; set; } 
    public float Price { get; set; } 

    private Item(DataRow row) 
    { 
     if (row != null) 
     { 
      ItemId = long.Parse(row[_ItemIdDataName].ToString()); 
      ItemName = row[_ItemNameDataName].ToString(); 
      Price = float.Parse(row[_PriceDataName].ToString()); 
     } 
    } 

    public Item[] load() 
    { 
     DataTable dt=DBHandler.GetItems();//Stored procedure that returns DataTable 
     Item[] items = new Item[dt.Rows.Count]; 
     for (int i = 0; i < dt.Rows.Count; i++) 
     { 
      items[i] = new Item(dt.Rows[i]); 
     } 
     return items; 
    } 
} 

estoy haciendo las cosas bien? ¿Cómo puedo mejorar esto?

+2

Por qué el load() 'clase de artículos en el interior? –

+0

Porque no tiene sentido clasificarlo en una clase diferente. Tengo muchos casos como este y no puedo tener cientos de clases que solo devuelven el resultado de la consulta. – Naor

+1

@Nor lo hacen estático al menos .. – nawfal

Respuesta

23

Si solo vas a usarlo una vez, probablemente sea correcto, pero si lo haces mucho deberías tratar de hacer algunas cosas más genéricas. Escribí una publicación de blog sobre cómo escribir un método de extensión para DataTable que crea una lista de objetos. Funciona mediante la convención de que las propiedades del objeto deben tener el mismo nombre que las columnas en el procedimiento almacenado (me gustaría cambiar el nombre en el procedimiento almacenado si pudiera):

public static class DataTableExtensions 
{ 
    public static IList<T> ToList<T>(this DataTable table) where T : new() 
    { 
     IList<PropertyInfo> properties = typeof(T).GetProperties().ToList(); 
     IList<T> result = new List<T>(); 

     foreach (var row in table.Rows) 
     { 
      var item = CreateItemFromRow<T>((DataRow)row, properties); 
      result.Add(item); 
     } 

     return result; 
    } 

    public static IList<T> ToList<T>(this DataTable table, Dictionary<string, string> mappings) where T : new() 
    { 
     IList<PropertyInfo> properties = typeof(T).GetProperties().ToList(); 
     IList<T> result = new List<T>(); 

     foreach (var row in table.Rows) 
     { 
      var item = CreateItemFromRow<T>((DataRow)row, properties, mappings); 
      result.Add(item); 
     } 

     return result; 
    } 

    private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new() 
    { 
     T item = new T(); 
     foreach (var property in properties) 
     { 
      property.SetValue(item, row[property.Name], null); 
     } 
     return item; 
    } 

    private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties, Dictionary<string, string> mappings) where T : new() 
    { 
     T item = new T(); 
     foreach (var property in properties) 
     { 
      if(mappings.ContainsKey(property.Name)) 
       property.SetValue(item, row[mappings[property.Name]], null); 
     } 
     return item; 
    } 
} 

Ahora puede simplemente llamar

var items = dt.ToList<Item>(); 

o

var mappings = new Dictionary<string,string>(); 
mappings.Add("ItemId", "item_id"); 
mappings.Add("ItemName ", "item_name"); 
mappings.Add("Price ", "price); 
var items = dt.ToList<Item>(mappings); 

La entrada del blog está aquí: http://blog.tomasjansson.com/2010/11/convert-datatable-to-generic-list-extension

Hay muchas formas en que puede ampliar esto, puede incluir algún tipo de diccionario de mapeo que indique a la extensión cómo mapear las columnas, de esa forma no es necesario que los nombres coincidan. O puede agregar una lista de nombres de propiedad que le gustaría excluir en la asignación.

Actualización: Su objeto (Item) que está creando debe tener un constructor predeterminado, de lo contrario, el método privado no podrá crearlo. Dado que la forma en que funciona la solución es primero crear el objeto que usar las propiedades que se obtienen de la reflexión para establecer los valores del objeto.

Actualización 2: He añadido la parte con el diccionario de asignaciones, pero no lo he probado, por lo que podría no compilarse. Sin embargo, el concepto está ahí y creo que funciona.

+0

Buena respuesta. Reusability + –

+0

Gracias ... Trataba de mantenerlo simple, y no es tan difícil hacer coincidir las propiedades de su objeto con las columnas en el procedimiento almacenado :). –

+1

Aquí supone que los nombres de los campos db son iguales a los nombres de los miembros. Lo que no siempre es verdad ¿Me equivoco? – Naor

5

Bastante bueno, pero tengo algunas sugerencias:

  • No eche cosas toString ser analizado de nuevo a otro tipo. Esto puede dañar su tipo de datos , y es lento/ineficiente.

  • Espere y verifique si el nulo proviene de SQL Server.

Así, en lugar de:

ItemId = long.Parse(row[_ItemIdDataName].ToString()); 

Probar:

ItemId = row.Field<long?>(_ItemIdDataName) ?? value_if_null; 

(Agregar una referencia a System.Data.DatasetExtensions para obtener la extensión de campo)

0

Su constructor de artículos privada y no parecen las funciones load() a pertenecen en su clase Item.

Ya sabes, una clase debería hacer una cosa y una cosa bien.

Así que trate de
1. refactor la c'tor privado a un decir una clase de ayuda que simplemente se analiza el DataRow y devuelve una instancia del artículo 2.
y refactorizar el load() en una clase diferente que utiliza simplemente el método de ayuda anterior y devuelve una matriz de instancias del objeto Item

+0

Esta forma me hará crear muchas clases. Quiero unir las cosas si hay una conexión. Me quedé sin nombres para las clases. – Naor

+0

@Naor: Estoy respondiendo sus preguntas principales;) – Sung

2
+0

¡Amigo! ¡¡2 AÑOS!! – Naor

+2

@Naor De hecho, pero esto se preguntó mucho después de que existiera AutoMapper, y esto todavía aparece en las búsquedas de Google en busca de una respuesta. StackOverflow es para encontrar las mejores respuestas, no para garantizar que la conversación se una bien y luego se cierre; si eso es lo que deseaba, use Google Groups. –

+0

esta respuesta debe marcarse como "Respuesta" –

Cuestiones relacionadas