Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting error while using Db.Async.query method #45

Closed
AnalyzeNCode opened this issue Feb 11, 2022 · 6 comments
Closed

Getting error while using Db.Async.query method #45

AnalyzeNCode opened this issue Feb 11, 2022 · 6 comments

Comments

@AnalyzeNCode
Copy link

Hi @pimbrouwers, thanks for this amazing library. I was really looking for such kind of lightweight ADO.Net wrapper library.
I was doing some experiment with the library and notice that whenever I use Db.Async.query, I couldn't find the proper way to manage the Task-Result returned by the query. Actually, the async & task is always confusing for me.

This is not actually a bug and but just want some example of using Db.Async.query with SQL Server and handling the output to chain in pipeline of Async<Result<'a, DbError>>.

Actually, I tried with Async.AwaitTask and then Async.RunSynchronously but then started getting random error of "Invalid operations: the connection is closed" or "Invalid Operations: the data reader is closed.".
The reason of these errors might be because of the Async.query has already executed and the connection and datareader have been already closed, but the RunSynchronously is trying to execute method and fetch data.

There are some open issues in Microsoft.Data.SqlClient regarding the Async usage as well, but what I observe is, currently I am getting the errors because of my implementation of code, and once I will be able to execute code successfully then might be started to facing those issues.

@pimbrouwers
Copy link
Owner

Hello again Subhash!

The test project shows some very basic ways you can use the async functionality, for example:

let sql = "
    SELECT author_id, full_name
    FROM   author
    WHERE  author_id IN (1,2)"

let authors = 
    conn
    |> Db.newCommand sql
    |> Db.Async.query Author.FromReader
    |> Async.AwaitTask
    |> Async.RunSynchronously

authors
|> List.map (printfn "%A")

However, if you are fortunate enough to be using FSharp.Core version 6. Then you can do:

let sql = "
    SELECT author_id, full_name
    FROM   author
    WHERE  author_id IN (1,2)"

task {
    let! authors = 
        conn
        |> Db.newCommand sql
        |> Db.Async.query Author.FromReader

    authors
    |> List.map (printfn "%A")
}

@AnalyzeNCode
Copy link
Author

Thanks for the example. I will try at my end and will let you know.
Thanks again @pimbrouwers

@AnalyzeNCode
Copy link
Author

AnalyzeNCode commented Feb 12, 2022

Hi @pimbrouwers
I have a question in mind regarding the Async based implementation in your library.

You have use Async keyword like ""Db.Async.query" but the result it return is Task.

As per my understanding, the database-access is I/O operations and so it's falls under Asynchronous workflow, rather than Thread & Task based workflow which are best for CPU bound works.

Any specific reason to return Task? Maybe you are using the F# 6 task based implementation.

@AnalyzeNCode
Copy link
Author

AnalyzeNCode commented Feb 12, 2022

I have tested according to your examples. It's working according to first example, which is Async.AwaitTask and then RunSynchronously but failing with error "Connection is closed" for the task {} of F# 6.

And Async.RunSynchronously will block the main thread while executing the request to I don't think it's helpful to execute an asynchronous task. I mean it's almost like a synchronous execution.

Please note that, the code is in repository method and I am returning this method result to the another method in workflow.
The gist of my code:

Repository.fs

   exception PersistenceException of DbError

   let raisePersistenceException dbErr = raise (PersistenceException dbErr)

    type ApplicationDAO = { Id: Guid; Name: string }
    
    let toDbOkOrRaise (taskResult: Task<Result<'a, DbError>>) =
        task {
            let! result = taskResult
    
            return
                match result with
                | Ok data -> data
                | Error dbErr -> dbErr |> raisePersistenceException
        }

    let ofDataReader (dbReader: IDataReader) : ApplicationDAO =
        let id = dbReader.ReadGuid "Id"
        let name = dbReader.ReadString "Name"
        { Id = id; Name = name }

    let getApplicationsByName =
        fun applicationName->
            let sqlQuery =
                "SELECT Id, Name FROM [ApplicationMaster] WHERE [Name] = @appName"
    
            let param =
                [ "appName", SqlType.String(applicationName) ]
           
            use conn = new SqlConnection(ConnectionString)
    
            task {
                let! result =
                    conn
                    |> Db.newCommand sqlQuery
                    |> Db.setParams param
                    |> Db.Async.query ofDataReader
                    |> toDbOkOrRaise
    
                return result
            }

Workflow.fs

let GetDbResult() =
    task { return! Repository.getApplicationsByName "TestApplication" }

@AnalyzeNCode AnalyzeNCode reopened this Feb 12, 2022
@pimbrouwers
Copy link
Owner

Hi again!

The problem is the "use" statement. You should pass the connection in as a param to the function allowing external control of the connection.

@AnalyzeNCode
Copy link
Author

Hi @pimbrouwers, That works.
Thanks for your help. I tried every other way but nothing worked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants