【问题标题】:LINQ Select All Columns From 2 DataTables and Return as 1 DataTable VB.NETLINQ 从 2 个数据表中选择所有列并作为 1 个数据表返回 VB.NET
【发布时间】:2018-09-15 06:08:30
【问题描述】:

不幸地把我的头发扯掉了。

简而言之,我有一个 DataTable 包含要完成的数据,然后在第二个 DataTable 我有操作的结果。 (已完成、未完成等)

我需要将两组信息连同LEFT OUTER JOIN 一起返回到DataGridView

这是我目前得到的:

Dim Query = From t1 In MasterTbl Group Join t2 In MasterActionTbl On t1.Field(Of String)("FreshAppsID") Equals t2.Field(Of String)("FreshAppsID") Into ps = Group From p In ps.DefaultIfEmpty()
                Select t1

    Return Query.CopyToDataTable

当我尝试这样做时它失败了:

Select t1, t2

我基本上希望使用左外连接返回来自 t1 和 t2 的所有信息,因为 t2 中可能不存在针对 t1 中的所有值的任何“动作”记录。

我查看了 DataRelation,但是这不允许将所有数据返回到同一个 DataGridView

TLDR

想要从两个数据表中选择信息,使用左外连接将它们连接在一起,然后将它们作为单个数据表返回以在 datagridview 中使用。

很多

【问题讨论】:

  • 您正在寻找一种不存在的神奇解决方案。 CopyToDataTable 方法只能用于 DataRow 对象列表。它基本上是一种创建另一个DataTable 的过滤副本的方法。您需要做的是使用您需要的列显式创建一个新的DataTable,然后循环遍历 LINQ 查询的结果。在您调用NewRow 的循环内,填充行,然后调用Rows.Add
  • 您可以编写自己的扩展方法来执行我在内部建议的操作,然后在查询结果中调用它。您可能可以在 wen 上找到许多这样的方法,事实上,我几天前就发布了一个。问题是,这些方法通常在IEnumerable(Of T) 上使用反射来根据T 的属性确定列。这将要求您更改 Select 子句以单独选择每个值,而不是选择两个 DataRows 整体。
  • @jmcilhinney 当我执行“选择 t1”时它返回一切都很好,如果连接正常,那么 t2 应该与连接的 t1 位于相同的 DataRow 上,返回都是一样的。它们只是一个 Datarow。
  • 我特别指出CopyToDataTable 仅适用于IEnumerable(Of DataRow)。如果您使用Select t1,那么假设t1DataRow 类型,结果将是IEnumerable(Of DataRow)。当然可以。如果您使用Select t1, t2,那么您将返回一个IEnumerable(Of T),其中T 不是DataRow。它包含两个DataRow 对象,所以显然这不是DataRow。我希望它是一个匿名类型,有两个DataRow 类型的属性,而不是DataRow
  • 你可以找到我最近发布的ToDataTable方法here。不过我说过,您必须更改 Select 子句以单独选择每个所需的字段,例如Select New With {.Name = t1.Field(Of String)("Name"), .Number = t2.Field(Of Integer)("Number")}.

标签: vb.net linq


【解决方案1】:

使用一些扩展,您可以使用一种方法,将DataRows 的匿名对象中的结果合并到一个新的DataTable 中。我写了一个答案here,但这使用了我学到的一些新技术:

Public Module SomeExt
    <System.Runtime.CompilerServices.Extension()>
    Public Function GetTypedValue(Of TProp)(ByVal p As PropertyInfo, obj As Object) As TProp
        Return DirectCast(p.GetValue(obj), TProp)
    End Function

    ' Create new DataTable from LINQ results on DataTable
    ' Expect T to be anonymous object of form new { DataRow d1, DataRow d2, ... }
    <System.Runtime.CompilerServices.Extension()>
    Public Function FlattenToDataTable(Of T)(src As IEnumerable(Of T)) As DataTable
        Dim res = New DataTable()
        If src.Any Then
            Dim firstRow = src.First()
            Dim rowType = firstRow.GetType()
            Dim memberInfos = rowType.GetProperties.Cast(Of MemberInfo).Concat(rowType.GetFields).ToList
            Dim allDC = memberInfos.SelectMany(Function(mi) mi.GetValue(Of DataRow)(firstRow).Table.DataColumns())

            For Each dc In allDC
                Dim newColumnName = dc.ColumnName
                If res.ColumnNames.Contains(newColumnName) Then
                    Dim suffixNumber = 1
                    While (res.ColumnNames.Contains($"{newColumnName}.{suffixNumber}"))
                        suffixNumber += 1
                    End While
                    newColumnName = $"{newColumnName}.{suffixNumber}"
                End If
                res.Columns.Add(New DataColumn(newColumnName, dc.DataType))
            Next

            For Each objRows In src
                res.Rows.Add(memberInfos.SelectMany(Function(mi) mi.GetValue(Of DataRow)(objRows).ItemArray).ToArray())
            Next
        End If
        Return res
    End Function

    ' ***
    ' *** DataTable Extensions
    ' ***
    <System.Runtime.CompilerServices.Extension()>
    Public Function DataColumns(ByVal aTable As DataTable) As IEnumerable(Of DataColumn)
        Return aTable.Columns.Cast(Of DataColumn)
    End Function
    <System.Runtime.CompilerServices.Extension()>
    Public Function ColumnNames(ByVal aTable As DataTable) As IEnumerable(Of String)
        Return aTable.DataColumns.Select(Function(dc) dc.ColumnName)
    End Function

    ' ***
    ' *** MemberInfo Extensions
    ' ***
    <System.Runtime.CompilerServices.Extension()>
    Public Function GetValue(ByVal 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)} but is of type {member.GetType}")
        End If
    End Function
    <System.Runtime.CompilerServices.Extension()>
    Public Function GetValue(Of T)(ByVal member As MemberInfo, srcObject As Object) As T
        Return DirectCast(member.GetValue(srcObject), T)
    End Function

End Module

有了这个扩展,你只需join你的DataTables然后转换答案:

Dim Query = From t1 In MasterTbl
            Group Join t2 In MasterActionTbl On t1.Field(Of String)("FreshAppsID") Equals t2.Field(Of String)("FreshAppsID") Into ps = Group _
            From p In ps.DefaultIfEmpty()
            Select New With { t1, t2 }

Return Query.FlattenToDataTable

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 2017-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多