如何使用 LINQ 从 DataTable 中以 IEnumerable<MODEL> 形式获取数据?

Posted

技术标签:

【中文标题】如何使用 LINQ 从 DataTable 中以 IEnumerable<MODEL> 形式获取数据?【英文标题】:How to get data as IEnumerable<MODEL> from DataTable by using LINQ? 【发布时间】:2020-12-08 11:21:19 【问题描述】:

我有一个DataTable,其中一些属性数据来自数据库,DataTable 中的每个数据都属于一个产品,每个产品可以有多个属性。

所以我有另一个DataTable,它在foreach 中包含所有产品,通过循环遍历每一行,我将每个产品添加到List&lt;Plu&gt;,如下所示:

var productAttr = new List<Plu.Attributi>();
foreach (DataRow rowPlu in dt.Rows)

    try
    
        int id = (int)rowPlu["ID_PLUREP"];
        plu.Add(new Plu(
            id,
            (string)rowPlu["CODICE_PRP"],
            (string)rowPlu[ESTESA],
            (string)rowPlu[DESCR], (float)rowPlu["PRE_PRP"],
            rowPlu.IsNull("IMG_IMG") ? null : (string)rowPlu["IMG_IMG"],
            productAttr,
            null,
            (int)rowPlu["ID_MENU_PRP"]
            ));
    
    catch
    
        return plu;
    

现在productAttr 是空的,但现在我需要将它的属性添加到每个产品中,所以通过以下函数我得到一个DataTable,其中填充了数据库中包含所有产品属性的数据:

var attributi = Attributi(connection, idNegozio);

然后我试图在foreach 中做这样的事情

foreach (DataRow rowPlu in dt.Rows)

    try
    
        int id = (int)rowPlu["ID_PLUREP"];
        plu.Add(new Plu(
            id,
            (string)rowPlu["CODICE_PRP"],
            (string)rowPlu[ESTESA],
            (string)rowPlu[DESCR], (float)rowPlu["PRE_PRP"],
            rowPlu.IsNull("IMG_IMG") ? null : (string)rowPlu["IMG_IMG"],
            from row in attributi.AsEnumerable() where row.Field<int>("ID_PLUREP_VAT") == id select row,
            null,
            (int)rowPlu["ID_MENU_PRP"]
            ));
    
    catch
    
        return plu;
    

但是 LINQ 返回一个EnumerableRowCollection 而我需要一个IEnumerable&lt;Plu.Attribute&gt;,所以我想知道是否有一种懒惰的方式将.AsEnumerable 转换为IEnumerable&lt;Plu.Attrbute&gt;...

【问题讨论】:

.Cast&lt;Plu.Attrbute&gt;() ? 【参考方案1】:

问题是,DataTable 只知道单元格中的值。它不知道这些值代表什么。它不知道第 0 列中的数字实际上是一个 Id。它不知道第 1 列中的字符串是客户的姓名,而第 2 列中的 DateTime 是客户的生日。

如果您将来要将此 Datatable(或类似 DataTables)的内容用于其他查询,则需要从 DataRow 转换为它们所代表的项目。

在完成从 DataRow 到 Plu 的转换后,您可以将 DataTable 转换为 IEnumerable&lt;Plu&gt;,并对其进行其他 LINQ 处理。

用法如下:

DataTable table = ...
var mySelectedData = table.AsEnumerable().ToPlus()
    .Where(plu => ...)
    .Select(plu => new ...)
    .ToList();

您需要两种扩展方法:一种将 DataRow 转换为 Plu,另一种将 DataRow 序列转换为 Plus 序列。见extension methods demystified

public static Plu ToPlu(this DataRow row)

    // TODO implement


public static IEnumerable<Plu> ToPlus(this IEnumerable<DataRow> dataRows)

     // TODO: exception if null dataRows

     return dataRows.Select(row => row.ToPlu());

如果需要,从 DataTable 创建一个扩展方法来提取 Plus:

public static IEnumerable<Plu> ExtractPlus(this DataTable table)

    // TODO: exception if table null
    return table.AsEnumerable().ToPlus();

用法:

DataTable table = ...
IEnumerable<Plu> plus = table.ExtractPlus();

我对 Plu 是什么一无所知,而且您忘记提及 Plu 的相关属性,所以我将给您举一个包含客户的表的示例:

class Customer

    public int Id get; set;              // Id will be in column 0
    public string Name get; set;         // Name will be in column 1
    ...


public static Customer ToCustomer(this DataRow row)

    return new Customer
    
        Id = (int)row[0],
        Name = (string)row[1],
    ;

如果需要,您可以使用列名代替 columnIndex。

因此,只需创建一个ToPlu 和一个将 DataRows 序列转换为 Plus 序列的单行方法,您就可以使用读取表的方法扩展 LINQ。

为了安全起见,请考虑创建一个将 Plus 序列转换为 DataTable 的扩展方法。这样,表格的布局就在一个位置:ToPlu(DataRow)ToDataRow(Plu)。表格布局的未来变化将更易于管理,您的 DataTable 的用户只会考虑 Plus 的序列。

【讨论】:

【参考方案2】:

您可以执行以下操作。如果你想要IEnumerable&lt;Plu&gt;,你可以从末尾删除.ToList()

dt.AsEnumerable().Select(x => new Plu 
   Id = x.Field<int>("ID_PLUREP"),
   CodicePrep = x.Field<string>("CODICE_PRP"),
   ....
   Attributes = attributi.AsEnumerable()
                         .Where(y => y.Field<int>("ID_PLUREP_VAT") == x.Field<int>("ID_PLUREP"))
                         .Select(z => new Attributi 
                                      
                                        ....
                                      ).ToList(),
   ....
).ToList();

【讨论】:

以上是关于如何使用 LINQ 从 DataTable 中以 IEnumerable<MODEL> 形式获取数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何从DataTable和LINQ获取分组的字段数组

如何使用自己的 DataTable 类“Linq to DataTable”

使用 DataContext 从 LINQ 查询中填充 DataTable 的最快方法

从两个通过 LINQ 连接的 DataTable 创建组合的 DataTable。 C#

如何计算 LINQ 中 DataTable 的列的总和(到数据集)?

从 Linq 查询返回类型化的 DataTable