将LINQ查询结果放入DataTable

时间:2018-01-30 11:15:24

标签: c# linq datatable

我有这个问题:

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

我的问题是如何转换查询以使其工作?我试图转换为(DataRow)LINQ的结果,但它没有用。

3 个答案:

答案 0 :(得分:1)

在您的LINQ查询中,您尝试获取IEnumerable<DataRow>作为结果,但实际上您选择了匿名类型的新对象: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查询中调用一个方法,该方法读取类似于确定性函数,但实际上会创建在数据行上设置列值的副作用,但它对我来说并不合适。

答案 1 :(得分:0)

这可以帮到你。

定义表格结构。

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

enter image description here

答案 2 :(得分:-1)

您可以使用此方法:

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