如何使用F#查询表达式进行“包含”查询?

6

如何编写类似于 SQL IN 查询的查询表达式?

我正在尝试做类似以下的事情:

let customerNumbers = set ["12345"; "23456"; "3456"]
let customerQuery = query {
    for c in dataContext.Customers do
    where(customerNumbers.Contains(c.CustomerNumber))
    select c
}

但是我遇到了一个错误:

System.NotSupportedException: Method 'Boolean Contains(System.String)' has no supported translation to SQL.

查看 http://msdn.microsoft.com/en-us/library/hh225374.aspx 中的查询表达式文档,我应该使用另一个查询来处理包含部分,但是这段代码不起作用,示例已经损坏:

// Select students where studentID is one of a given list.
let idQuery = query { for id in [1; 2; 5; 10] do select id }
query { 
    for student in db.Student do
    where (idQuery.Contains(student.StudentID))
    select student
}

idQuery实际上不包含任何"Contains"方法。

我也尝试过:

let customerNumbers = set ["12345"; "23456"; "3456"]
let customerQuery = query { 
    for c in dataContext.Customers do
    where (query { for x in customerNumbers do exists (c.CustomerNumber=x)})
    select r
}

但是这会产生以下错误信息:

System.NotSupportedException: Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator

我在进一步测试后注意到,除了Gene的建议之外,以下方法也可以正常工作:

let customerNumbers = set ["12345"; "23456"; "3456"]
query {
    for customer in dataContext.Customer do
    where (query { for x in customerNumbers do contains customer.CustomerNumber})
    select customer
}

我刚刚注意到,如果我将集合替换为列表,则我的第一个示例似乎奇怪地可以工作。 - NeoDarque
你介意在获取异常时包含完整的堆栈跟踪吗?这不会帮助回答你的问题,但我很想知道它是在 F# 查询 -> LINQ 翻译层还是在 LINQ -> SQL 翻译中出现了错误。 - Jack P.
@JackP: 这里是 - Gene Belitski
此功能在 SQLProvider 中得到支持:https://github.com/fsprojects/SQLProvider - Tuomas Hietanen
1个回答

7
我认为问题出在 F# 的 Set 实现 Contains 方法的方式上。它属于 ICollection 接口,这个事实有点让 LINQ-to-SQL 查询构建器感到不安。
如果你将 Contains 显式地强制转换为 IEnumerable 扩展方法,一切都会变得正常:
let customerNumbers = set ["12345"; "23456"; "3456"]
let customerQuery = query {
    for c in dataContext.Customers do
    where((customerNumbers :> IEnumerable<string>).Contains(c.CustomerNumber))
    select c
}

或者等价地,您可以添加非LINQ-to-SQL查询

let idQuery = query { for id in customerNumbers do select id }

这个程序没有问题,可以枚举set并产生seq<string>,然后将其用于Contains

....
where (idQuery.Contains(c.CustomerNumber))
....

或者,您可以将customerNumbers保留为seq

let customerNumbers = set ["12345"; "23456"; "3456"] |> Set.toSeq

并将其用作直觉提示:

....
where(customerNumbers.Contains(c.CustomerNumber))
....

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接