【问题标题】:Convert DataTable to IEnumerable<T>将 DataTable 转换为 IEnumerable<T>
【发布时间】:2011-03-24 11:38:49
【问题描述】:

我正在尝试将 DataTable 转换为 IEnumerable。其中 T 是我创建的自定义类型。我知道我可以通过创建List&lt;T&gt; 来做到这一点,但我在想是否有一种更巧妙的方法可以使用 IEnumerable 来做到这一点。这是我现在拥有的:

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

关键部分是我正在创建一个List&lt;T&gt;,然后使用AsEnumerable() 返回它。

【问题讨论】:

    标签: c# datatable ienumerable


    【解决方案1】:

    该实现没有错。你可以试试yield 关键字,看看你喜欢它:

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

    AsEnumerable 也不是必需的,因为 List&lt;T&gt; 已经是 IEnumerable&lt;T&gt;

    【讨论】:

      【解决方案2】:

      还有一个名为“AsEnumerable()”的 DataSetExtension 方法(在 System.Data 中),它接受一个 DataTable 并返回一个 Enumerable。有关更多详细信息,请参阅the MSDN doc,但它基本上很简单:

      dataTable.AsEnumerable()
      

      缺点是它枚举 DataRow,而不是您的自定义类。但是,“Select()”LINQ 调用可以转换行数据:

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

      【讨论】:

      • 哦,不错。我没有注意到dataTable.AsEnumerable(),而且总是做得更长更丑:dataTable.Rows.Cast&lt;DataSetName.SomeLongDataTableRowName&gt;()
      • AsEnumerable 扩展方法位于 System.Data 命名空间中,但请务必引用 System.Data.DataSetExtensions 程序集以使用它。
      • 对数据视图使用 .Cast() 扩展。
      • 如果您使用的是旧版本的 .NET,您还可以通过 dataTable.Cast&lt;DataRow&gt;() 获取 IEnumerable
      【解决方案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; 
      

      【讨论】:

        【解决方案4】:

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

        或者:

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

        或者,更好的是,创建一个TankReading(DataRow r) 构造函数,然后变成:

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

        【讨论】:

          【解决方案5】:

          如果您从 SQL 查询中生成 DataTable,您是否考虑过简单地使用 Dapper?

          然后,而不是使用 SqlParametersDataTableDataAdapter 来制作 SqlCommand 等等,然后您必须费力地将其转换为类,您只需定义类,制作查询列名与字段名匹配,参数通过名称轻松绑定。您已经定义了 TankReading 类,所以它真的很简单!

          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;
          

          现在是不是很棒? Dapper 进行了非常优化,可以为您提供与直接使用DataAdapter 阅读相当的性能。

          如果你的类有任何逻辑或者是不可变的或者没有无参数的构造函数,那么你可能确实需要一个DbTankReading 类(作为一个纯 POCO/Plain Old Class 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; }
          }
          

          你会这样使用它:

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

          尽管映射工作,这仍然没有数据表方法那么痛苦。这也可以让您执行某种逻辑,但如果逻辑不仅仅是非常简单的直接映射,我会将逻辑放入单独的 TankReadingMapper 类中。

          【讨论】:

            【解决方案6】:

            DataTable 的通用扩展方法。可能有人会很有趣。我从另一篇文章中获取创建动态属性的想法: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;
                        }
                    }
                }
            

            【讨论】:

              【解决方案7】:

              如果您想将任何 DataTable 转换为等效的 IEnumerable 向量函数。

              请看一下下面的通用函数,这可能对您的需求有所帮助(您可能需要根据您的需要包含针对不同数据类型的编写案例)。

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

              【讨论】:

                【解决方案8】:

                我在here 上写了一篇关于这个主题的文章。 我想它可以帮助你。

                通常它会这样做:

                static void Main(string[] args)
                {
                    // Convert from a DataTable source to an IEnumerable.
                    var usersSourceDataTable = CreateMockUserDataTable();
                    var usersConvertedList = usersSourceDataTable.ToEnumerable<User>();
                
                    // Convert from an IEnumerable source to a DataTable.
                    var usersSourceList = CreateMockUserList();
                    var usersConvertedDataTable = usersSourceList.ToDataTable<User>();
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-09-30
                  • 2018-02-04
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-02-24
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多