尝试做一些像这样简单的事情:
context.Users.Any(fun currentUser -> currentUser.UserName = userName)
Context只是一个实体框架上下文。现在,当我将鼠标悬停在“currentUser”上时,它知道它是用户类型。但是我得到了:
根据之前的信息查找不确定类型的对象 这个计划点。在此之前可能需要类型注释 程序指向约束对象的类型。这可能允许 查找要解决。
现在我意识到我可以做到这一点:
context.Users.Any(fun (currentUser:User) -> currentUser.UserName = userName)
但这似乎很愚蠢,因为c#很容易推断出类型:
context.Users.Any(currentUser => currentUser.UserName = userName)
完整的方法是:
let FindAndRemoveUser(userName:String, context:StoryBoardContext) =
if context.Users.Any(fun currentUser-> currentUser.UserName = userName) then
let foundUser = context.Users.Where(fun innerUser -> innerUser.UserName = userName).First()
context.Users.DeleteObject(foundUser)
context.SaveAll() |> ignore
我认为F#应该处理类型推断还是比C#更好还是错误的吗?
答案 0 :(得分:4)
我认为您的方法比您描述的问题更具根本性。当您在C#中将Where
或Any
与lambda表达式一起使用时,C#编译器会将lambda转换为表达式树Expression<Func<_, _>>
,因此LINQ to Entities可以将代码转换为SQL查询。
但是,当您使用F#lambda函数作为参数时,它将被编译为函数(或类型Func<_, _>
的委托)。这意味着您的代码将调用处理函数的内存版本,并且您将在内存中执行所有处理,而不是在数据库服务器上执行此操作!
要在F#2.0中编写查询,您需要将所有代码包装在引号中并使用F# PowerPack中的query
函数运行它(F#3.0将使这更好,但那是不幸的是只是一个测试版)。你可能需要这样的东西:
if query <@ context.Users |> Seq.exists (fun currentUser ->
currentUser.UserName = userName) @> then
let foundUser =
query <@ context.Users
|> Seq.filter (fun usr -> usr.UserName = userName)
|> Seq.head @>
context.Users.DeleteObject(foundUser)
context.SaveAll() |> ignore
(除此之外,我不确定您是否需要检查用户是否预先存在 - 您可以只使用filter
查找所有用户,然后如果返回的序列包含某些内容则删除第一个用户)
答案 1 :(得分:2)
我认为context.Users
是seq<User>
,因此您可以在Seq
模块上使用高阶函数。与Linq相比,您将受益于F#序列中的类型推断:
let FindAndRemoveUser(userName:String, context:StoryBoardContext) =
if context.Users |> Seq.exists (fun currentUser -> currentUser.UserName = userName) then
let foundUser = context.Users |> Seq.filter (fun innerUser -> innerUser.UserName = userName) |> Seq.head
context.Users.DeleteObject(foundUser)
context.SaveAll() |> ignore
在Linq和F#序列here中有一个关于类型推断的有趣线程。