2010-08-02 13 views
36

Próbuję przekonwertować DataTable na IEnumerable. Gdzie T jest niestandardowym typem, który stworzyłem. Wiem, że mogę to zrobić, tworząc listę, ale myślałem, że był to najlepszy sposób, aby to zrobić za pomocą IEnumerable. Oto, co mam teraz.Konwertuj DataTable na 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(); 
    } 

Kluczową częścią jest to, że tworzę listę, a następnie zwracam ją przy pomocy AsEnumerable().

Odpowiedz

42

Nic złego w tym wdrożeniu. Można podać słowa kluczowego yield strzał, zobaczyć, jak ci się podoba:

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"]), 
            }; 
     } 

    } 

Również AsEnumerable nie jest konieczne, ponieważ List<T> jest już IEnumerable<T>

+0

Matt Greer dzięki za odpowiedź. To wygląda dobrze. Myślę, że spróbuję i zobaczę, co się stanie. – mpenrow

+0

Ta odpowiedź zadziałała bardzo dobrze. Dzięki! –

52

Istnieje również metoda DataSetExtension zwany „AsEnumerable() "(w System.Data), który pobiera DataTable i zwraca wartość Enumerable. Zobacz the MSDN doc więcej szczegółów, ale to w zasadzie tak proste, jak:

dataTable.AsEnumerable() 

Minusem jest to, że wyliczanie DataRow, a nie swoją klasę niestandardową. A "Wybierz()" wezwanie LINQ mogłyby przekształcić dane wierszy, jednakże:

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 dziękuję za pomoc. DataTable.AsEnumerable jest bardzo interesująca. Muszę to zbadać. – mpenrow

+0

Oooh, miłe. Nie zauważyłem 'dataTable.AsEnumerable()', i zawsze robi to dłużej, brzydsze: 'dataTable.Rows.Cast ()' –

+5

Opcja 'AsEnumerable' metodę rozszerzenia znajduje się w' System. Przestrzeń nazw danych, ale pamiętaj, aby odwołać się do zestawu 'System.Data.DataSetExtensions', aby go użyć. – 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

Czy możesz wyjaśnić, co ten kod robi? Czy ten kod został skopiowany? – quantum

+0

Czy możesz wyjaśnić, co faktycznie robi twój kod? –

1

Prosta metoda z użyciem 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"]), 
    }); 

Lub:

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)); 

Albo lepiej jednak, stwórz konstruktora TankReading(DataRow r), to staje się:

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

Jeśli produkujesz DataTable z zapytania SQL, czy uważasz, że zamiast tego używasz Dappera?

Następnie, zamiast tworzenia SqlCommand z SqlParameters i DataTable i DataAdapter i tak dalej, które następnie trzeba mozolnie konwertować do klasy, wystarczy zdefiniować klasy sprawiają, że nazwy kolumn zapytania pasują nazwy pól , a parametry są wiązane łatwo po nazwie. Masz już zdefiniowaną klasę TankReading, więc będzie to naprawdę proste!

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; 

Czy to nie jest niesamowite? Dapper jest bardzo zoptymalizowany i zapewni Ci porównywalną wydajność porównywalną z czytaniem bezpośrednio z DataAdapter.

Jeśli klasa ma żadnej logiki w nim w ogóle lub jest niezmienna i nie ma konstruktora bez parametrów, to prawdopodobnie trzeba mieć klasę DbTankReading (jako czystego POCO/Plain Old klasy Object):

// 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; } 
} 

byłoby użyć, że tak:

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 
    }); 

Pomimo prac odwzorowania, to jeszcze mniej bolesne niż metoda tabeli danych.To pozwala także wykonać jakąś logikę, choć jeśli logika jest więcej niż bardzo proste prosto w poprzek mapowania, chciałbym umieścić logikę do osobnego TankReadingMapper klasie.

0

uniwersalna metoda rozszerzenie dla DataTable. Może być kimś interesującym. Idea tworzenia właściwości dynamiczne biorę z innym poście: 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

Jeśli chcesz przekonwertować dowolny DataTable do podobnej funkcji wektora IEnumerable.

Proszę spojrzeć na następującej funkcji generycznego, może to pomóc swoich potrzeb (może być konieczne w celu uwzględnienia przypadków zapisu dla różnych typów danych w zależności od potrzeb).

/// <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(); 
    } 
Powiązane problemy