首页 > 解决方案 > F#查询异常,“无法识别的方法调用值”

问题描述

问:以下异常是什么意思?

System.Exception: 'unrecognised method call value(FSharp.Data.Sql.Runtime.QueryImplementation+SqlQueryable`1[FSharp.Data.Sql.Common.SqlEntity]).GroupJoin(value(FSharp.Data.Sql.Runtime.QueryImplementation+SqlQueryable`1[FSharp.Data.Sql.Common.SqlEntity]), arClientRow => arClientRow.GetColumn("CLIENT_ID"), dsItemRow => dsItemRow.GetColumn("CLIENT_ID"), (arClientRow, dsItemGroup) => new AnonymousObject`2(Item1 = arClientRow, Item2 = dsItemGroup.DefaultIfEmpty()))'

我正在尝试使用SqlDataProviderODBC 连接将 Pervasive SQL 查询转换为 F# 查询,一点一点地建立起来。我在以下查询中遇到了一个异常,其中有一个有点神秘的异常。

let Q2KQuery = query {
    for arClientRow in Q2KDb.Dbo.ArClient do
    leftOuterJoin dsItemRow in Q2KDb.Dbo.DsItem 
        on (arClientRow.ClientId = dsItemRow.ClientId) 
        into dsItemGroup
    select (arClientRow, dsItemGroup) (*select statement simplified here for demonstration purposes*)
}

printfn "%i" (Seq.length Q2KQuery)

printfn执行语句并实际计算表达式时,会遇到异常:

System.Exception: 'unrecognised method call value(FSharp.Data.Sql.Runtime.QueryImplementation+SqlQueryable`1[FSharp.Data.Sql.Common.SqlEntity]).GroupJoin(value(FSharp.Data.Sql.Runtime.QueryImplementation+SqlQueryable`1[FSharp.Data.Sql.Common.SqlEntity]), arClientRow => arClientRow.GetColumn("CLIENT_ID"), dsItemRow => dsItemRow.GetColumn("CLIENT_ID"), (arClientRow, dsItemGroup) => new AnonymousObject`2(Item1 = arClientRow, Item2 = dsItemGroup.DefaultIfEmpty()))'

这是一个更大的查询的一部分,它在执行时具有完全相同的异常:

let Q2KQuery = query {
    for arClientRow in Q2KDb.Dbo.ArClient do
    leftOuterJoin dsItemRow in Q2KDb.Dbo.DsItem 
        on (arClientRow.ClientId = dsItemRow.ClientId) 
        into dsItemGroup
    for dsItemRow in dsItemGroup do
    leftOuterJoin dsWhseInvHeaderRow in Q2KDb.Dbo.DsWhseInvHeader
        on ((dsItemRow.ClientId, dsItemRow.ItemId) = (dsWhseInvHeaderRow.ClientId, dsWhseInvHeaderRow.ItemId))
        into dsWhseInvHeaderGroup
    for dsWhseInvHeaderRow in dsWhseInvHeaderGroup do
    where (
        dsItemRow.ObsoleteFlg <> "Y"
        && arClientRow.IsInactive <> "Y"
        && dsWhseInvHeaderRow.WhseId = "B1"
        && dsItemRow.InvunitQty = 1
    )
    select (arClientRow, dsItemRow, dsWhseInvHeaderRow) (*select statement simplified here for demonstration purposes*)
}

仅供参考,此 F# 查询所转换的 SQL 查询如下(该SELECT语句不如JOIN引发 F# 异常的子句重要):

SELECT
-- LOTS of selects here... --
-- ... --
----- the important part of the F# queries begin here -----
FROM 
    ArClient
    LEFT JOIN DsItem 
    ON ArClient.CLIENT_ID = DsItem.CLIENT_ID
    LEFT JOIN DsWhse_Inv_Header 
    ON (DsItem.CLIENT_ID = DsWhse_Inv_Header.CLIENT_ID)
    AND (DsItem.ITEM_ID = DsWhse_Inv_Header.ITEM_ID)
WHERE (
    ((DsItem.OBSOLETE_FLG) <> 'Y')
    AND ((ArClient.IS_INACTIVE) <> 'Y')
    AND ((DsWhse_Inv_Header.WHSE_ID) = 'B1')
    AND ((DsItem.INVUNIT_QTY) = 1)
    )
ORDER BY
    CONCAT(LEFT(DsItem.CLIENT_ID, 4), RTRIM(LTRIM(DsItem.ITEM_ID)));

标签: sql.netexceptionf#pervasive

解决方案


在阅读了一些 SO 答案和 GitHub 问题(链接如下)之后,目前似乎不支持以我正在执行的方式对这种 SQL 数据库进行的确切操作(也许?至少据我了解读),虽然显然query可以使用运算符在表达式中进行左连接!!- 但我还没有成功。

然而,我发现以下笨拙的解决方法使用了query我一直使用的相同表达式,通过首先将数据转换为List(或Array等)来强制将数据读入内存:

let ArClient = Seq.toList <| query {
    for row in Q2KDb.Dbo.ArClient do
    where (row.IsInactive <> "Y")
    select row
}

let DsItem = Seq.toList <| query {
    for row in Q2KDb.Dbo.DsItem do
    where (row.ObsoleteFlg <> "Y" && row.InvunitQty = 1)
    select row
}

let DsWhseInvHeader = Seq.toList <| query {
    for row in Q2KDb.Dbo.DsWhseInvHeader do
    where (row.WhseId = "B1")
    select row
}

然后可以query以最小的变化在原始表达式中使用它。

let results = Seq.toList <| query {
    for arClientRow in ArClient do
    leftOuterJoin dsItemRow in DsItem
        on (arClientRow.ClientId = dsItemRow.ClientId) 
        into dsItemGroup
    for dsItemRow in dsItemGroup do
    leftOuterJoin dsWhseInvHeaderRow in DsWhseInvHeader
        on ((dsItemRow.ClientId, dsItemRow.ItemId) = (dsWhseInvHeaderRow.ClientId, dsWhseInvHeaderRow.ItemId))
        into dsWhseInvHeaderGroup
    for dsWhseInvHeaderRow in dsWhseInvHeaderGroup do
    select (arClientRow (*etc etc*))

这当然是非常低效的(读取 62k+ 行DsItem使用大约 500MB+ 的内存并且需要 30+ 秒才能读取),但它确实有效......


链接:

SO:F# groupBy - System.Exception:无法识别的方法调用

GitHub:左外连接上的“无法识别的方法调用值”#588

GitHub:连接排序可能会导致运行时异常 (!!) #614

GitHub:leftOuterJoin 导致 Postgresql #235 “无法识别的方法调用”


推荐阅读