2015-07-26 20 views
6

Ben F # SqlClient kullanarak ile oynamak ve ben SqlDataReader.ReadAsync kullanarak zorluk yaşıyorum olduğu zaman uyumsuz hesaplama ifadesinde. Ben'iken' koşulu zaman uyumsuz

while (await reader.ReadAsync) { ... }

F # bunu yapmanın en iyi yolu nedir F # eşdeğer yapmaya çalışıyorum? Aşağıda benim tam programım. Çalışıyor, ancak bunu yapmanın daha iyi bir yolu olup olmadığını bilmek istiyorum.

open System 
open System.Data.SqlClient 
open System.Threading.Tasks 

let connectionString = "Server=.;Integrated Security=SSPI" 

module Async = 
    let AwaitVoidTask : (Task -> Async<unit>) = 
     Async.AwaitIAsyncResult >> Async.Ignore 

    // QUESTION: Is this idiomatic F#? Is there a more generally-used way of doing this? 
    let rec While (predicateFn : unit -> Async<bool>) (action : unit -> unit) : Async<unit> = 
     async { 
      let! b = predicateFn() 
      match b with 
       | true -> action(); do! While predicateFn action 
       | false ->() 
     } 

[<EntryPoint>] 
let main argv = 
    let work = async { 
     // Open connection 
     use conn = new SqlConnection(connectionString) 
     do! conn.OpenAsync() |> Async.AwaitVoidTask 

     // Execute command 
     use cmd = conn.CreateCommand() 
     cmd.CommandText <- "select name from sys.databases" 
     let! reader = cmd.ExecuteReaderAsync() |> Async.AwaitTask 

     // Consume reader 

     // I want a convenient 'while' loop like this... 
     //while reader.ReadAsync() |> Async.AwaitTask do // Error: This expression was expected to have type bool but here has type Async<bool> 
     // reader.GetValue 0 |> string |> printfn "%s" 
     // Instead I used the 'Async.While' method that I defined above. 

     let ConsumeReader = Async.While (fun() -> reader.ReadAsync() |> Async.AwaitTask) 
     do! ConsumeReader (fun() -> reader.GetValue 0 |> string |> printfn "%s") 
    } 
    work |> Async.RunSynchronously 
    0 // return an integer exit code 

cevap

7

Eğer
do! While predicateFn action kullanarak yinelemeli çağrı yapıyoruz ki kodunuzu bir sorun vardır. Bu bir sorundur çünkü bir kuyruk çağrısına dönüşmez ve böylece bellek sızıntılarına neden olabilirsiniz. Bunu yapmanın doğru yolu, do! yerine return! kullanmaktır. Kenara

, kodunuz iyi çalışır. Ama aslında sıradan while anahtar kelime kullanmasına izin async hesaplama oluşturucu uzatabilir. İşte

let rec While (predicateFn : unit -> Async<bool>) (action : Async<unit>) : Async<unit> = 
    async { 
     let! b = predicateFn() 
     if b then 
      do! action 
      return! While predicateFn action 
    } 

type AsyncBuilder with 
    member x.While(cond, body) = Async.While cond body 

beden için de asenkron ve bir fonksiyon değildir: Bunu yapmak için, While biraz farklı bir sürümü gerekir. Daha sonra hesaplama oluşturucu bir While yöntemi ekleyin (bu yüzden bir uzantı yöntemi gibi başka bir aşırı yük ilave edilir). Bununla aslında yazabilirsiniz:

while Async.AwaitTask(reader.ReadAsync()) do // This is async! 
    do! Async.Sleep(1000) // The body is asynchronous too 
    reader.GetValue 0 |> string |> printfn "%s" 
0

Muhtemelen seninle aynı şeyi yapardım. Gerçi ref mide varsa,

let go = ref true 
while !go do 
    let! more = reader.ReadAsync() |> Async.AwaitTask 
    go := more 
    reader.GetValue 0 |> string |> printfn "%s" 
kadar kısaltın olabilir