Проблема определения типа F # с linq и, возможно, структурой объекта - PullRequest
2 голосов
/ 23 ноября 2011

Просто пытаюсь сделать что-то простое, как это:

context.Users.Any(fun currentUser -> currentUser.UserName = userName)

Где Context - это просто контекст структуры объекта. Теперь, когда я наведу курсор на «currentUser», он знает, что это тип User. Однако я получаю:

Поиск по объекту неопределенного типа на основе информации до эта программная точка. До этого может потребоваться аннотация типа программная точка для ограничения типа объекта. Это может позволить поиск должен быть решен.

Теперь я понимаю, что могу сделать это:

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 #?

Ответы [ 2 ]

4 голосов
/ 23 ноября 2011

Я думаю, что у вашего подхода есть более фундаментальная проблема, чем просто проблема, которую вы описали.Когда вы используете Where или Any с лямбда-выражением в C #, компилятор C # превращает лямбду в дерево выражений Expression<Func<_, _>>, и поэтому LINQ to Entities может преобразовать код в запрос SQL.

Однако, когда вы используете лямбда-функцию F # в качестве аргумента, она будет скомпилирована как функция (или делегат типа Func<_, _>).Это означает, что ваш код будет вызывать версию функции обработки в памяти, и вы будете выполнять всю обработку в памяти вместо того, чтобы делать это на сервере базы данных!

Чтобы написать запрос в F # 2.0, вам нужнооберните весь код внутри цитаты и запустите его, используя функцию query из F # PowerPack (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, а затем удалитьпервый, если возвращаемая последовательность содержит что-то)

2 голосов
/ 23 ноября 2011

Полагаю, 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 # здесь .

...