【问题标题】:Put LINQ query result to DataTable将 LINQ 查询结果放入 DataTable
【发布时间】:2018-01-30 11:15:23
【问题描述】:

我有这个问题:

        var smallExchangeReport =  from ex in exchangeProgReport
                                   where !string.IsNullOrEmpty(ex.comment)
                                   group ex by new { ex.siteName } into g
                                   select new SummuryReportTraffic
                                   {
                                       siteName = g.Key.siteName,
                                       exchangeCounter = g.Where(x => x.Prog1ToProg2Check == 1).Count(),
                                       descriptions = (from t in g
                                                       group t by new { t.comment, t.siteName } into grp
                                                       select new Description
                                                       {
                                                           title = grp.Key.comment,
                                                           numbers = grp.Select(x => x.comment).Count()
                                                       })
                                   };

在某些时候,我使用 foreach 循环将其放入 dataTable:

             foreach (var item in smallExchangeReport)
            {
                dr = smrTable.NewRow();

                foreach (var d in item.descriptions)
                {
                    dr[d.title] = d.numbers;
                }

                smrTable.Rows.Add(dr);
            }

但我需要在不使用 foreach 循环的情况下将 LINQ 结果放入 dataTable。 所以我根据这个link对上面的代码做了一些修改:

        DataTable dt = new DataTable();
        DataRow dr = dt.NewRow();

        IEnumerable<DataRow> smallExchangeReport =  from ex in exchangeProgReport.AsEnumerable()
                                   where !string.IsNullOrEmpty(ex.comment)
                                   group ex by new { ex.siteName } into g
                                   select new 
                                   {
                                       siteName = g.Key.siteName,
                                       exchangeCounter = g.Where(x => x.Prog1ToProg2Check == 1).Count(),
                                       descriptions = (from t in g.AsEnumerable()
                                                       group t by new { t.comment, t.siteName } into grp
                                                       select new
                                                       {
                                                           title = grp.Key.comment,
                                                           numbers = grp.Select(x => x.comment).Count()
                                                       })
                                   };

    // Create a table from the query.
    DataTable boundTable = smallExchangeReport.CopyToDataTable<DataRow>();

但在更改 LINQ 查询时,我收到此错误:

 Cannot implicitly convert type:'System.Collections.Generic.IEnumerable<<anonymous type: string siteName, int exchangeCounter>>' to 
     'System.Collections.Generic.IEnumerable<System.Data.DataRow>'. An explicit conversion exists (are you missing a cast?)

我的问题是如何强制转换查询以使其工作?我尝试将 LINQ 的结果强制转换为(DataRow),但没有成功。

【问题讨论】:

    标签: c# linq datatable


    【解决方案1】:

    在您的 LINQ 查询中,您试图获得 IEnumerable&lt;DataRow&gt; 作为结果,但实际上您选择了匿名类型的新对象:select new { siteName = .... }。这不起作用,因为您的匿名类型无法转换为 DataRow

    您需要做的是使用一个可以像这样填充DataRow 的函数:

    DataRow PopulateDataRow(
        DataTable table, 
        string siteName, 
        int exchangeCounter, 
        IEnumerable<Description> descriptions
    {
        var dr = table.NewRow();  
    
        // populate siteName and exchangeCounter
        // (not sure how your data row is structured, so I leave it to you)
    
        foreach (var d in descriptions)
        {
            dr[d.title] = d.numbers;
        }
    
        return dr;
    }
    

    然后在您的 LINQ 查询中,按如下方式使用它:

    IEnumerable<DataRow> smallExchangeReport =  
        from ex in exchangeProgReport.AsEnumerable()
        where !string.IsNullOrEmpty(ex.comment)
        group ex by new { ex.siteName } into g
        select PopulateDataRow(
            smrTable,
            siteName: g.Key.siteName,
            exchangeCounter: g.Where(x => x.Prog1ToProg2Check == 1).Count(),
            descriptions: (from t in g.AsEnumerable()
                group t by new { t.comment, t.siteName } into grp
                select new Description {
                    title = grp.Key.comment,
                    numbers = grp.Select(x => x.comment).Count()
                }
            )
        );
    

    这个解决方案去掉了一个foreach(在行上)并留下另一个(在描述上)。

    如果删除第二个foreach 很重要...我仍然会将其留在PopulateDataRow 中。我没有看到删除它的优雅方法。您可以从 LINQ 查询中调用一个方法,该方法读起来像一个确定性函数,但实际上会产生在数据行上设置列值的副作用,但我觉得不合适。

    【讨论】:

    • felix 感谢发帖。为什么不能将匿名类型转换为 DataRow?
    • 因为匿名类型不继承DataRow;它继承 System.Object。匿名类型只是一个普通的类类型,只是你没有声明它——C# 编译器从“new{...}”语句推导出它的声明,它总是从 System.Object 继承的。除此之外,它的作用与任何其他类完全一样。假设您声明“class MyClass { ... }”。您不会期望它被强制转换为 DataRow,因为 MyClass 不继承 DataRow。
    • 如果我声明 MyClass{...} 我可以通过 upcsating 使其成为 DataRow(仅当它继承 DataRow 时)?
    • 如果您声明“class MyClass : DataRow {...}”,那么它将在必要时隐式转换为 DataRow
    【解决方案2】:

    这可以帮助你。

    定义表结构。

    DataTable tbl = new DataTable();
    tbl.Columns.Add("Id");
    tbl.Columns.Add("Name");
    

    我们需要从匿名类型创建数据行。

        Func<object, DataRow> createRow = (object data) =>
        {
            var row = tbl.NewRow();
            row.ItemArray = data.GetType().GetProperties().Select(a => a.GetValue(data)).ToArray();
            return row;
        };
    

    使用虚假查询进行测试:

        var enumarate = Enumerable.Range(0, 10);
        var rows = from i in enumarate
                   select createRow( new { Id = i, Name = Guid.NewGuid().ToString() });
        var dataTable  = rows.CopyToDataTable<DataRow>();
    

    【讨论】:

      【解决方案3】:

      你可以使用这个方法:

          private DataTable ListToDataTable<T>(List<T> objs, string tableName) {
              var table = new DataTable(tableName);
              var lists = new List<List<object>>();
              // init columns
              var propertyInfos = new List<PropertyInfo>();
              foreach (var propertyInfo in typeof(T).GetProperties()) {
                  propertyInfos.Add(propertyInfo);
                  if(propertyInfo.PropertyType.IsEnum || propertyInfo.PropertyType.IsNullableEnum()) {
                      table.Columns.Add(propertyInfo.Name, typeof(int));
                  } else {
                      table.Columns.Add(propertyInfo.Name, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType);
                  }
                  table.Columns[table.Columns.Count - 1].AllowDBNull = true;
              }
              // fill rows
              foreach(var obj in objs) {
                  var list = new List<object>();
                  foreach(var propertyInfo in propertyInfos) {
                      object currentValue;
                      if(propertyInfo.PropertyType.IsEnum || propertyInfo.PropertyType.IsNullableEnum()) {
                          var val = propertyInfo.GetValue(obj);
                          if(val == null) {
                              currentValue = DBNull.Value;
                          } else {
                              currentValue = (int)propertyInfo.GetValue(obj);
                          }
                      } else {
                          var val = propertyInfo.GetValue(obj);
                          currentValue = val ?? DBNull.Value;
                      }
                      list.Add(currentValue);
                  }
                  lists.Add(list);
              }
              lists.ForEach(x => table.Rows.Add(x.ToArray()));
              return table;
          }
      

      编辑:

      使用了这个扩展方法:

          public static bool IsNullableEnum(this Type t) {
              var u = Nullable.GetUnderlyingType(t);
              return u != null && u.IsEnum;
          }
      

      【讨论】:

      • OP 要求使用 LINQ 并摆脱 foreach 循环。 “我需要在不使用 foreach 循环的情况下将 LINQ 结果放入 dataTable”。你的回答没有做这些。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多