我发现使用linq查询命令的DataTable
结果的复杂分组的所有示例在获取IEnumerable(Of DataRow)
对象时都没有问题。
但是我似乎只得到AnonymousType Enumerator
返回,我无法转发DataTable
。
我有解决方法,但我希望将结果转换为DataTable
,因为它看起来可能并且我可能做错了。
它是一个包含许多ClientID
和ClientName
列的简单表,其他列包含登录时间戳。
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)
答案 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 DataColumn
s 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()