【问题标题】:VB.NET LINQ to DataSet (SQL 'LEFT OUTER JOIN' alternative)VB.NET LINQ to DataSet(SQL 'LEFT OUTER JOIN' 替代方案)
【发布时间】:2018-05-15 13:42:05
【问题描述】:

我想加入两个数据表并根据结果创建第三个。结果 DataTable 应该有三列:

  • 身份证
  • 姓名
  • 出生年份

我的编译选项:

  • 选项显式:打开
  • 选项严格:开启
  • 选项比较:二进制
  • 选项推断:关闭

    Dim dr As DataRow
    
    REM Dt1
    Dim Dt1 As New DataTable
    Dt1.Columns.Add("ID", GetType(Integer))
    Dt1.Columns.Add("Name", GetType(String))
    
    dr = Dt1.NewRow
    dr("ID") = 1
    dr("Name") = "Peter"
    Dt1.Rows.Add(dr)
    
    dr = Dt1.NewRow
    dr("ID") = 2
    dr("Name") = "Anna"
    Dt1.Rows.Add(dr)
    
    dr = Dt1.NewRow
    dr("ID") = 3
    dr("Name") = "John"
    Dt1.Rows.Add(dr)
    REM End Dt1
    
    REM Dt2
    Dim Dt2 As New DataTable
    Dt2.Columns.Add("ID", GetType(Integer))
    Dt2.Columns.Add("YearOfBirth", GetType(Integer))
    
    dr = Dt2.NewRow
    dr("ID") = 1
    dr("YearOfBirth") = 1970
    Dt2.Rows.Add(dr)
    
    dr = Dt2.NewRow
    dr("ID") = 2
    dr("YearOfBirth") = 1980
    Dt2.Rows.Add(dr)
    REM End Dt2
    
    Dim Dt3 As New DataTable
    
    Dim query As IEnumerable(Of DataRow) = From dr1 In Dt1.AsEnumerable()
                                           Group Join dr2 In Dt2.AsEnumerable()
                                           On dr1.Field(Of Integer)("ID") Equals dr2.Field(Of Integer)("ID")
                                           Into joined = Group
                                           From j In joined.DefaultIfEmpty()
                                           Select New With
                                               {
                                               .ID = dr1.Field(Of Integer)("ID"),
                                               .Name = dr1.Field(Of String)("Name"),
                                               .YearOfBirth = j.Field(Of Integer)("YearOfBirth")
                                               }
    
    
    Dt3 = query.CopyToDataTable
    

但我在编辑器中收到错误消息(VS 2017):

“错误 BC36754: “IEnumerable(匿名类型:ID 作为整数,名称作为字符串,YearOfBirth 作为整数)”无法转换为“IEnumerable(数据行)”,因为“匿名类型:ID 作为整数,名称作为字符串,YearOfBirth 作为整数”不是从 'DataRow' 派生,根据 'Interface IEnumerable(Of Out T)' 中的 'Out' 泛型参数 'T' 的要求。”

【问题讨论】:

  • 您不能致电CopyToDataTable,除非您的列表包含DataRows,而您的列表中没有。您需要自己创建DataTable,然后遍历查询结果,为每个项目添加DataRow
  • 谢谢,我的查询有误。
  • 如果有人可以为我编写正确的部分代码(Dim 查询...),那对我很有帮助。

标签: vb.net linq dataset


【解决方案1】:

Select New不指定类名作为查询结果将返回匿名类型(如IEnumerable(Of AnonymousType)),CopyToDataTable()抛出异常,因为IEnumerable(Of AnonymousType)不能直接转换为IEnumerable(Of DataRow)

因此,您需要使用额外的Select 方法将匿名类型转换为DataRow,该方法迭代IEnumerable(Of AnonymousType) 内容并返回DataRowDataTable.NewRow()(使用准备好的DataTable,其中包括作为结果集的列名) :

' joined DataTable columns
Dim JoinedDT As New DataTable
JoinedDT.Columns.Add("ID", GetType(Integer))
JoinedDT.Columns.Add("Name", GetType(String))
JoinedDT.Columns.Add("YearOfBirth", GetType(Integer))

' other stuff

Dim query As IEnumerable(Of DataRow) = (From dr1 In Dt1.AsEnumerable() _
                                       Group Join dr2 In Dt2.AsEnumerable() _
                                       On dr1.Field(Of Integer)("ID") Equals dr2.Field(Of Integer)("ID") _
                                       Into joined = Group _
                                       From j In joined.DefaultIfEmpty() _
                                       Select New With
                                       {
                                           .ID = dr1.Field(Of Integer)("ID"),
                                           .Name = dr1.Field(Of String)("Name"),
                                           .YearOfBirth = If(j IsNot Nothing, j.Field(Of Integer)("YearOfBirth"), 0)
                                       }).Select(Function(r)
                                           ' use `DataTable.NewRow` here
                                           Dim row As DataRow = JoinedDT.NewRow()
                                           row("ID") = r.ID
                                           row("Name") = r.Name
                                           row("YearOfBirth") = r.YearOfBirth
                                           Return row
                                       End Function)

Dt3 = query.CopyToDataTable()

参考:

Get linq to return IEnumerable<DataRow> result(C#版本)

【讨论】:

  • 您的代码没问题,但前提是 DataTables Dt1 和 Dt2 都有 3 行 ID 为 1,2 和 3。如果 DataTable Dt2 只有两行 ID 为 1 和 2(没有ID 3),我收到错误:“System.ArgumentNullException: 'Value cannot be null. Parameter name: row'”。
  • 我刚刚发现 YearOfBirth 只包含 2 行(对于 ID 3,它设置为 Nothing)。使用带有 2 个参数的 If operator 来检查 Nothing 并在 DataRow 中包含 Nothing 值时返回默认整数值。
  • 如果我使用row("YearOfBirth") = If(r.YearOfBirth, 0),则查询不正确。我在错误列表中看到此错误:错误 BC33107:二进制“If”表达式中的第一个操作数必须为可为空或引用类型。如果我在代码部分使用.YearOfBirth = j.Field(Of Integer)("YearOfBirth")Object,而不是Integer,则查询正式OK,但启动后出现错误:“System.ArgumentNullException: 'Value cannot be null. Parameter name: row ”。如果我使用Try...Catch,我看到错误就在Select New With这一行。
  • 好吧,我把Nothing 的支票放错地方了。正如你所说,它应该放在Select New With,也由this issue确认。如有任何误解,请见谅。
  • 现在查询很好。你帮了我很多。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2021-06-11
  • 2011-08-03
  • 1970-01-01
  • 2010-12-13
  • 1970-01-01
  • 2014-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多