2010-08-02 35 views
36

Estoy tratando de convertir una DataTable en IEnumerable. Donde T es un tipo personalizado que creé. Sé que puedo hacerlo mediante la creación de una lista, pero me pareció que había una forma más sencilla de hacerlo con IEnumerable. Esto es lo que tengo ahora.Convertir DataTable a IEnumerable <T>

private IEnumerable<TankReading> ConvertToTankReadings(DataTable dataTable) 
    { 
     var tankReadings = new List<TankReading>(); 
     foreach (DataRow row in dataTable.Rows) 
     { 
      var tankReading = new TankReading 
            { 
             TankReadingsID = Convert.ToInt32(row["TRReadingsID"]), 
             TankID = Convert.ToInt32(row["TankID"]), 
             ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]), 
             ReadingFeet = Convert.ToInt32(row["ReadingFeet"]), 
             ReadingInches = Convert.ToInt32(row["ReadingInches"]), 
             MaterialNumber = row["MaterialNumber"].ToString(), 
             EnteredBy = row["EnteredBy"].ToString(), 
             ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]), 
             MaterialID = Convert.ToInt32(row["MaterialID"]), 
             Submitted = Convert.ToBoolean(row["Submitted"]), 
            }; 
      tankReadings.Add(tankReading); 
     } 
     return tankReadings.AsEnumerable(); 
    } 

La parte clave es que estoy creando una Lista y luego regresándola usando AsEnumerable().

Respuesta

42

Nada de malo con esa implementación. Usted puede dar la yield palabra clave un tiro, ver cómo te gusta:

private IEnumerable<TankReading> ConvertToTankReadings(DataTable dataTable) 
    { 
     foreach (DataRow row in dataTable.Rows) 
     { 
      yield return new TankReading 
            { 
             TankReadingsID = Convert.ToInt32(row["TRReadingsID"]), 
             TankID = Convert.ToInt32(row["TankID"]), 
             ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]), 
             ReadingFeet = Convert.ToInt32(row["ReadingFeet"]), 
             ReadingInches = Convert.ToInt32(row["ReadingInches"]), 
             MaterialNumber = row["MaterialNumber"].ToString(), 
             EnteredBy = row["EnteredBy"].ToString(), 
             ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]), 
             MaterialID = Convert.ToInt32(row["MaterialID"]), 
             Submitted = Convert.ToBoolean(row["Submitted"]), 
            }; 
     } 

    } 

También el AsEnumerable no es necesario, ya que List<T> es ya un IEnumerable<T>

+0

Matt Greer gracias por su respuesta. Esto luce bien. Creo que lo probaré y veré qué pasa. – mpenrow

+0

Esta respuesta funcionó muy bien para mí. ¡Gracias! –

52

También hay un método DataSetExtension llamado "AsEnumerable() "(en System.Data) que toma una DataTable y devuelve un Enumerable. Ver the MSDN doc para más detalles, pero básicamente es tan fácil como:

dataTable.AsEnumerable() 

La desventaja es que está enumerando DataRow, no la clase personalizada. Un "Seleccionar()" llamada de LINQ podría convertir los datos de la fila, sin embargo:

private IEnumerable<TankReading> ConvertToTankReadings(DataTable dataTable) 
{ 
    return dataTable.AsEnumerable().Select(row => new TankReading  
      {  
       TankReadingsID = Convert.ToInt32(row["TRReadingsID"]),  
       TankID = Convert.ToInt32(row["TankID"]),  
       ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]),  
       ReadingFeet = Convert.ToInt32(row["ReadingFeet"]),  
       ReadingInches = Convert.ToInt32(row["ReadingInches"]),  
       MaterialNumber = row["MaterialNumber"].ToString(),  
       EnteredBy = row["EnteredBy"].ToString(),  
       ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]),  
       MaterialID = Convert.ToInt32(row["MaterialID"]),  
       Submitted = Convert.ToBoolean(row["Submitted"]),  
      }); 
} 
+0

JaredReisinger gracias por su ayuda. El dataTable.AsEnumerable es muy interesante. Tendré que investigar eso. – mpenrow

+0

Oooh, lindo. No había notado 'dataTable.AsEnumerable()', y siempre estaba haciendo más tiempo, más feo: 'dataTable.Rows.Cast ()' –

+5

El método de extensión 'AsEnumerable' se encuentra en el' Sistema. Espacio de nombres de datos, pero asegúrese de hacer referencia al ensamblado 'System.Data.DataSetExtensions' para usarlo. – jhappoldt

-3
 PagedDataSource objPage = new PagedDataSource(); 

     DataView dataView = listData.DefaultView; 
     objPage.AllowPaging = true; 
     objPage.DataSource = dataView; 
     objPage.PageSize = PageSize; 

     TotalPages = objPage.PageCount; 

     objPage.CurrentPageIndex = CurrentPage - 1; 

     //Convert PagedDataSource to DataTable 
     System.Collections.IEnumerator pagedData = objPage.GetEnumerator(); 

     DataTable filteredData = new DataTable(); 
     bool flagToCopyDTStruct = false; 
     while (pagedData.MoveNext()) 
     { 
      DataRowView rowView = (DataRowView)pagedData.Current; 
      if (!flagToCopyDTStruct) 
      { 
       filteredData = rowView.Row.Table.Clone(); 
       flagToCopyDTStruct = true; 
      } 
      filteredData.LoadDataRow(rowView.Row.ItemArray, true); 
     } 

     //Here is your filtered DataTable 
     return filterData; 
+3

¿Puede explicarme qué hace este código? ¿Este código está copiado? – quantum

+0

¿Puedes explicarnos qué hace realmente tu código? –

1

método simple usando System.Data.DataSetExtensions:

table.AsEnumerable().Select(row => new TankReading{ 
     TankReadingsID = Convert.ToInt32(row["TRReadingsID"]), 
     TankID = Convert.ToInt32(row["TankID"]), 
     ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]), 
     ReadingFeet = Convert.ToInt32(row["ReadingFeet"]), 
     ReadingInches = Convert.ToInt32(row["ReadingInches"]), 
     MaterialNumber = row["MaterialNumber"].ToString(), 
     EnteredBy = row["EnteredBy"].ToString(), 
     ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]), 
     MaterialID = Convert.ToInt32(row["MaterialID"]), 
     Submitted = Convert.ToBoolean(row["Submitted"]), 
    }); 

O:

TankReading TankReadingFromDataRow(DataRow row){ 
    return new TankReading{ 
     TankReadingsID = Convert.ToInt32(row["TRReadingsID"]), 
     TankID = Convert.ToInt32(row["TankID"]), 
     ReadingDateTime = Convert.ToDateTime(row["ReadingDateTime"]), 
     ReadingFeet = Convert.ToInt32(row["ReadingFeet"]), 
     ReadingInches = Convert.ToInt32(row["ReadingInches"]), 
     MaterialNumber = row["MaterialNumber"].ToString(), 
     EnteredBy = row["EnteredBy"].ToString(), 
     ReadingPounds = Convert.ToDecimal(row["ReadingPounds"]), 
     MaterialID = Convert.ToInt32(row["MaterialID"]), 
     Submitted = Convert.ToBoolean(row["Submitted"]), 
    }; 
} 

// Now you can do this 
table.AsEnumerable().Select(row => return TankReadingFromDataRow(row)); 

O, mejor sin embargo, cree un constructor TankReading(DataRow r), luego se convierte en:

table.AsEnumerable().Select(row => return new TankReading(row)); 
1

Si está produciendo el DataTable a partir de una consulta SQL, ¿ha considerado simplemente usar Dapper?

Entonces, en vez de hacer una SqlCommand con SqlParameters y una DataTable y una DataAdapter y así sucesivamente, que luego se tiene que convertir laboriosamente a una clase, que acaba de definir la clase, hacer los nombres de columna consulta coinciden con los nombres de los campos y los parámetros están enlazados fácilmente por su nombre. Ya tiene definida la clase TankReading, ¡así que será realmente simple!

using Dapper; 

// Below can be SqlConnection cast to DatabaseConnection, too. 
DatabaseConnection connection = // whatever 
IEnumerable<TankReading> tankReadings = connection.Query<TankReading>(
    "SELECT * from TankReading WHERE Value = @value", 
    new { value = "tank1" } // note how `value` maps to `@value` 
); 
return tankReadings; 

¿Ahora no es tan asombroso? Dapper está muy optimizado y le dará un rendimiento casi equivalente, como leer directamente con un DataAdapter.

Si su clase tiene ninguna lógica en ella en absoluto, o es inmutable o no tiene ningún constructor sin parámetros, entonces es probable que tenga que tener una clase DbTankReading (como un objeto de clase POCO/Plain Old puro):

// internal because it should only be used in the data source project and not elsewhere 
internal sealed class DbTankReading { 
    int TankReadingsID { get; set; } 
    DateTime? ReadingDateTime { get; set; } 
    int ReadingFeet { get; set; } 
    int ReadingInches { get; set; } 
    string MaterialNumber { get; set; } 
    string EnteredBy { get; set; } 
    decimal ReadingPounds { get; set; } 
    int MaterialID { get; set; } 
    bool Submitted { get; set; } 
} 

que tendrá que utilizar la siguiente manera:

IEnumerable<TankReading> tankReadings = connection 
    .Query<DbTankReading>(
     "SELECT * from TankReading WHERE Value = @value", 
     new { value = "tank1" } // note how `value` maps to `@value` 
    ) 
    .Select(tr => new TankReading(
     tr.TankReadingsID, 
     tr.ReadingDateTime, 
     tr.ReadingFeet, 
     tr.ReadingInches, 
     tr.MaterialNumber, 
     tr.EnteredBy, 
     tr.ReadingPounds, 
     tr.MaterialID, 
     tr.Submitted 
    }); 

a pesar del trabajo de mapeo, esto sigue siendo menos doloroso que el método de la tabla de datos.Esto también te permite realizar algún tipo de lógica, aunque si la lógica no es más que un mapeo directo muy simple, pondría la lógica en una clase separada de TankReadingMapper.

0

Método de extensión universal para DataTable. Puede ser alguien ser interesante. Idea de crear propiedades dinámicas que llevan desde otro post: https://stackoverflow.com/a/15819760/8105226

public static IEnumerable<dynamic> AsEnumerable(this DataTable dt) 
    { 
     List<dynamic> result = new List<dynamic>(); 
     Dictionary<string, object> d; 
     foreach (DataRow dr in dt.Rows) 
     { 
      d = new Dictionary<string, object>(); 

      foreach (DataColumn dc in dt.Columns) 
       d.Add(dc.ColumnName, dr[dc]); 

      result.Add(GetDynamicObject(d)); 
     } 
     return result.AsEnumerable<dynamic>(); 
    } 

    public static dynamic GetDynamicObject(Dictionary<string, object> properties) 
    { 
     return new MyDynObject(properties); 
    } 

    public sealed class MyDynObject : DynamicObject 
    { 
     private readonly Dictionary<string, object> _properties; 

     public MyDynObject(Dictionary<string, object> properties) 
     { 
      _properties = properties; 
     } 

     public override IEnumerable<string> GetDynamicMemberNames() 
     { 
      return _properties.Keys; 
     } 

     public override bool TryGetMember(GetMemberBinder binder, out object result) 
     { 
      if (_properties.ContainsKey(binder.Name)) 
      { 
       result = _properties[binder.Name]; 
       return true; 
      } 
      else 
      { 
       result = null; 
       return false; 
      } 
     } 

     public override bool TrySetMember(SetMemberBinder binder, object value) 
     { 
      if (_properties.ContainsKey(binder.Name)) 
      { 
       _properties[binder.Name] = value; 
       return true; 
      } 
      else 
      { 
       return false; 
      } 
     } 
    } 
0

Si desea convertir cualquier DataTable a una función vectorial IEnumerable equivalente.

Por favor, observe la siguiente función genérica, esto puede ayudarlo (puede necesitar incluir casos de escritura para diferentes tipos de datos según sus necesidades).

/// <summary> 
    /// Get entities from DataTable 
    /// </summary> 
    /// <typeparam name="T">Type of entity</typeparam> 
    /// <param name="dt">DataTable</param> 
    /// <returns></returns> 
    public IEnumerable<T> GetEntities<T>(DataTable dt) 
    { 
     if (dt == null) 
     { 
      return null; 
     } 

     List<T> returnValue = new List<T>(); 
     List<string> typeProperties = new List<string>(); 

     T typeInstance = Activator.CreateInstance<T>(); 

     foreach (DataColumn column in dt.Columns) 
     { 
      var prop = typeInstance.GetType().GetProperty(column.ColumnName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); 
      if (prop != null) 
      { 
       typeProperties.Add(column.ColumnName); 
      } 
     } 

     foreach (DataRow row in dt.Rows) 
     { 
      T entity = Activator.CreateInstance<T>(); 

      foreach (var propertyName in typeProperties) 
      { 

       if (row[propertyName] != DBNull.Value) 
       { 
        string str = row[propertyName].GetType().FullName; 

        if (entity.GetType().GetProperty(propertyName).PropertyType == typeof(System.String)) 
        { 
         object Val = row[propertyName].ToString(); 
         entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, Val, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null); 
        } 
        else if (entity.GetType().GetProperty(propertyName).PropertyType == typeof(System.Guid)) 
        { 
         object Val = Guid.Parse(row[propertyName].ToString()); 
         entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, Val, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null); 
        } 
        else 
        { 
         entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, row[propertyName], BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null); 
        } 
       } 
       else 
       { 
        entity.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).SetValue(entity, null, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, null, null); 
       } 
      } 

      returnValue.Add(entity); 
     } 

     return returnValue.AsEnumerable(); 
    } 
Cuestiones relacionadas