【问题标题】:InvalidOperationException when joining on a composite key加入复合键时出现 InvalidOperationException
【发布时间】:2013-06-04 22:42:06
【问题描述】:

我有一个 LINQ 查询,它在调用 ToList() 时会在运行时引发 InvalidOperationException,如果可能的话,我试图找出解决方法,而不必将连接分解为单独的 where 子句。

在下面的示例中,有问题的行是第 3 行和第 4 行。 loan_id 是不可为空的十进制类型,ti_disb_id 是不可为空的短类型。当我将它们拆分为单独的语句时,调用 ToList() 时也不例外。然而,它似乎更有意义,所以如果有一个解决方案,我很想听听。 :)

        var baseQuery = (from a in context.ti_disbursements
                         join b in context.disbursement_schedule
                             on new {a.loan_id, a.ti_disb_id} equals
                                new {b.loan_id, b.ti_disb_id} into ab
                             from b in ab.DefaultIfEmpty()
                         join c in context.business_associates
                             on a.ba_id equals c.ba_id into ac
                             from c in ac.DefaultIfEmpty()
                         join d in context.investor_loan
                             on b.loan_id equals d.loan_id into ad
                             from d in ad.DefaultIfEmpty()
                         join e in context.loan_information
                             on d.loan_id equals e.loan_id into ae
                             from e in ae.DefaultIfEmpty()
                         join f in context.loan_balances
                             on e.loan_id equals f.loan_id into af
                             from f in af.DefaultIfEmpty()
                         join g in context.ti_information
                             on e.loan_id equals g.loan_id into ag
                             from g in ag.DefaultIfEmpty()
                         select new
                         {
                             loan_id = a.loan_id,
                             ti_disb_id = a.ti_disb_id,
                             ti_freq_id = a.ti_freq_id,
                             ti_disbursements_stop_code = a.ti_stop_code,
                             tax_account_number = a.tax_account_number,
                             ti_disb_due_dt = b.ti_disb_due_dt,
                             ti_expire_dt = b.ti_expire_dt,
                             ti_disb_amt = b.ti_disb_amt,
                             schedule_id = b.schedule_id,
                             ba_name = c.ba_name,
                             inv_bank_cd = d.inv_bank_cd,
                             inv_cd = d.inv_cd,
                             inv_group_cd = d.inv_group_cd,
                             loan_name = e.loan_name,
                             prin_bal = f.prin_bal,
                             ti_bal = f.ti_bal,
                             ti_information_stop_code = g.ti_stop_cd,
                             non_escrowed_type = a.non_escrowed_type,
                             ba_type_id = a.ba_type_id,
                             bill_received_dt = a.bill_received_dt,
                         });

异常信息:

用户代码未处理 InvalidOperation 异常

转换为值类型“Int16”失败,因为具体化值为空。结果类型的泛型参数或查询必须使用可为空的类型。

非常感谢!

【问题讨论】:

  • 对数据库运行查询以查看结果以及哪些列为空。这应该会给你一个关于 null 进入的地方的提示。
  • @GarrisonNeely 异常已经提供了所有这些信息(假设它实际上是从该查询的第 3 行或第 4 行抛出的),它表示 ti_disb_id 值之一为空。
  • @evanmcdonnal 这很有趣,实际上没有一个值是空的。我查看了数据库值,甚至使用了 supposed 生成的查询。它是否实际上使用该查询,我还没有弄清楚。

标签: c# .net sql linq


【解决方案1】:

继续 Garrison 的评论,我建议您使用上下文 Log 属性。 打开StreamWriter,并分配给context.Log。它将为您提供在数据库上运行的确切 SQL 查询,您可以运行它并查看问题出在哪里。

【讨论】:

  • 异常是说ti_dsib_id值之一为null,不用看查询。
  • @kobigurk 仍在研究如何获取 SQL,但这是我目前所拥有的:System.Data.Objects.ObjectQuery1[<>f__AnonymousType1320[System.Decimal,System.Int16,System.Int32 ,System.Nullable1[System.Int16],System.String,System.Nullable1[System.DateTime],System.Nullable1[System.DateTime],System.Nullable1[System.Decimal],System.Int16,System.String,System.String,System.String,System.String,System.String ,System.Nullable1[System.Decimal],System.Nullable1[System.Decimal],System.Nullable1[System.Int16],System.String,System.Nullable1[System.Int16],System.Nullable`1[System.DateTime]]]
【解决方案2】:

您可能想再看看您的数据库。例外情况是其中一条短裤返回为空(a.ti_disb_ida.ti_disb_id)。如有必要,您可以在查询中添加if 语句或使用空合并运算符。

我认为将new {a.loan_id, a.ti_disb_id} 更改为new { loan = a.loan_id, disb = a.ti_disb_id ?? 0} 将解决您的问题。空合并运算符基本上是说如果此值为空,则使用提供的默认值。为了使用它,需要赋值(我试过只是做a.ti_disb_id ?? 0,但它不会编译)这就是我输入属性名称的原因。参考文档是here

【讨论】:

  • 刚刚试过这个。由于某种原因,它不会使用空合并运算符 ?? 进行编译,所以我将其更改为使用三元运算符?如果为空,则替换为 0。在 ti_disb_id 和 loan_id 上都进行了测试,但仍然抛出异常。无论如何它们都是不可为空的,所以它们不应该有空值开始。最奇怪的。
  • @MacSigler 很不幸。您确定错误来自这些行之一吗?我相信如果你有一个null 值,任何使用这种初始化风格的尝试都会失败。编译器必须推断类型,它不能推断 null 的类型。我想如果我仍在尝试调试它,我会亲自开始查看 select 语句。它还创建了一个匿名类型,因此使用 null 的赋值也会导致失败。
  • 原来是 schedule_id,而不是 ti_disb_id,导致了异常。进一步调查。
  • @MacSigler 假设 b.schedule_id 在您分配时为空,适用相同的解决方案。
【解决方案3】:

所以,长话短说,经过多次调试,我发现问题在于我使用的是 LEFT JOIN 行为而不是 INNER JOIN 行为。

具体来说,对 DefaultIfEmpty() 的调用正在创建导致 schedule_id 的空值的行,它的类型是不可为空的 short (Int16),从而导致异常。通过取出这些 DefaultIfEmpty() 调用,不包括坏行,问题就解决了,因为 schedule_id 永远不会收到空值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多