如何从DataTable查询获取IEnumerable(Of DataRow)

时间:2018-03-22 10:26:09

标签: vb.net linq datatable ienumerable

我发现使用linq查询命令的DataTable结果的复杂分组的所有示例在获取IEnumerable(Of DataRow)对象时都没有问题。

但是我似乎只得到AnonymousType Enumerator返回,我无法转发DataTable
我有解决方法,但我希望将结果转换为DataTable,因为它看起来可能并且我可能做错了。

它是一个包含许多ClientIDClientName列的简单表,其他列包含登录时间戳。

Dim dtMatrix As DataTable = New DataTable()

...(填充DataTable)

Dim qClients = From row In dtMatrix 
  Group row By client = New With {Key .ClientID = row("ClientID"), Key .ClientName = row("ClientName")} Into Group 
  Select New With {Key .ClientID = client.ClientID, Key .ClientName = client.ClientName}

这将返回通用的枚举器结果,但是

Dim qClients As IEnumerable(Of DataRow) = From row In dtMatrix 
  Group row By client = New With {Key .ClientID = row("ClientID"), Key .ClientName = row("ClientName")} Into Group 
  Select New With {Key .ClientID = client.ClientID, Key .ClientName = client.ClientName}

引发异常

  

无法将类型对象...转换为类型   ' System.Collections.Generic.IEnumerable`1 [的System.Data.DataRow]'

如果能够增加更多清晰度,我将很乐意粘贴整个错误消息。

我的基本假设是DataTable应允许强制转换,因为它是被查询的对象。然而,情况似乎并非如此。我是否错误地构建了查询? (框架4.6.2)

2 个答案:

答案 0 :(得分:0)

您可以在OfType的{​​{1}}媒体资源上使用Rows

DataTable

Rows属性返回DataRowCollection,它实现(通过继承)Dim dtMatrix As DataTable = New DataTable() '' Populate code goes here... Dim dtRows As IEnumerable(Of DataRow) = dtMatrix.Rows.OfType(Of DataRow)() 接口而不是IEnumerable接口,这就是为什么你不能使用大多数linq直接在它上面。

答案 1 :(得分:0)

The following extension uses Reflection to create a new DataTable and create DataColumns in it that match the properties and fields of the type passed in. In general, if you are creating anonymous types in LINQ, you can't just convert to a DataRow which must be tied to a DataTable which must already have matching columns. I went ahead and wrote a second extension to DataTable that adds an IEnumerable<T> with matching field/property names to it.

Public Module Ext
    <Extension()>
    Public Function GetValue(member As MemberInfo, srcObject As Object) As Object
        If TypeOf member Is FieldInfo Then
            Return DirectCast(member, FieldInfo).GetValue(srcObject)
        ElseIf TypeOf member Is PropertyInfo Then
            Return DirectCast(member, PropertyInfo).GetValue(srcObject)
        Else
            Throw New ArgumentException("MemberInfo must be of type FieldInfo or PropertyInfo", Nameof(member))
        End If
    End Function

    <Extension()>
    Public Function GetMemberType(member As MemberInfo) As Type
        If TypeOf member Is FieldInfo Then
            Return DirectCast(member, FieldInfo).FieldType
        ElseIf TypeOf member Is PropertyInfo Then
            Return DirectCast(member, PropertyInfo).PropertyType
        ElseIf TypeOf member Is EventInfo Then
            Return DirectCast(member, EventInfo).EventHandlerType
        Else
            Throw New ArgumentException("MemberInfo must be of type FieldInfo, PropertyInfo or EventInfo", Nameof(member))
        End If
    End Function    

    <Extension()>
    Public Function ToDataTable(Of T)(rows As IEnumerable(Of T)) As DataTable
        Dim dt = New DataTable
        If (rows.Any()) Then
            Dim rowType = rows.First().GetType()
            Dim memberInfos = rowType.GetProperties.Cast(Of MemberInfo)().Concat(rowType.GetFields).ToArray()
            For Each info In memberInfos
                dt.Columns.Add(New DataColumn(info.Name, info.GetMemberType()))
            Next

            For Each r In rows
                dt.Rows.Add(memberInfos.Select(Function (i) i.GetValue(r)).ToArray())
            Next
        End If
        Return dt
    End Function

    <Extension()>
    Public Function AddObjects(Of T)(dt As DataTable, rows As IEnumerable(Of T))
        If (rows.Any()) Then
            Dim rowType = rows.First().GetType()
            Dim memberInfos = rowType.GetProperties().Cast(Of MemberInfo)().Concat(rowType.GetFields()).ToArray()

            For Each r In rows
                Dim newRow = dt.NewRow()
                For Each memberInfo In memberInfos
                    newRow(memberInfo.Name) = memberInfo.GetValue(r)
                Next

                dt.Rows.Add(newRow)
            Next
        End If
        Return dt
    End Function
End Module

Note that I write in C# and translated this from my C# extension. It is untested but compiles.

Using the extension, you should be able to get a DataTable from your qClients by:

Dim dtClients = qClients.ToDataTable()