Dapper.FSharp icon indicating copy to clipboard operation
Dapper.FSharp copied to clipboard

Correct way to create reusable queries

Open AP-Hunt opened this issue 1 year ago • 0 comments

Hi folks,

I've got an interface to implement with methods like

GetById(id) : User
GetByName(name): User
GetByEmail(email) : User
GetByPhoneNumber(phoneNo): User

An implementation for any one of these might look like below, and each of them varies only in the where clause.


member this.GetById(id: string) =
    conn.SelectAync<User> <| select {
        from u in usersTable
        where (u.Id = id)
    }

I tried to write a small reusable function (below) to tidy things up, but I got a NotImplementedException from LinqExpressionVisitors.visitWhere on line 260

Relevant stack trace

Dapper.FSharp.SQLite.LinqExpressionVisitors.visit$cont@243-4(FSharpFunc<MemberInfo, string> qualifyColumn, Expression exp, BinaryExpression x, Unit unitVar) in C:\Dzoukr\Personal\Dapper.FSharp\src\Dapper.FSharp\SQLite\LinqExpressionVisitors.fs
Dapper.FSharp.SQLite.LinqExpressionVisitors.visit@200-30(FSharpFunc<MemberInfo, string> qualifyColumn, Expression exp) in C:\Dzoukr\Personal\Dapper.FSharp\src\Dapper.FSharp\SQLite\LinqExpressionVisitors.fs
Dapper.FSharp.SQLite.LinqExpressionVisitors.visitWhere<T>(Expression<Func<T, bool>> filter, FSharpFunc<MemberInfo, string> qualifyColumn) in C:\Dzoukr\Personal\Dapper.FSharp\src\Dapper.FSharp\SQLite\LinqExpressionVisitors.fs
Dapper.FSharp.SQLite.Builders+SelectExpressionBuilder<T>.Where(QuerySource<T> state, Expression<Func<T, bool>> whereExpression) in C:\Dzoukr\Personal\Dapper.FSharp\src\Dapper.FSharp\SQLite\Builders.fs

let findFirstBy<'t when 't: null> (conn: DbConnection) (tbl: QuerySource<'t>)  (expr: 't -> bool) =
    task {
        let xs! = conn.SelectAsync<'t> <| select {
            for x in tbl do
            where (expr x)
        }
        
        match Seq.length xs with
        | 0 -> return null
        | _ -> return Seq.head xs        
    }
    
...

member this.GetById(id: string) = findFirstBy conn usersTable (fun u -> u.Id = id)
member this.GetByName(name: string) = findFirstBy conn usersTable (fun u -> u.Name = name)

What would be the correct way of implementing this? Or am I pursuing something that Dapper.FSharp wasn't designed to support?

AP-Hunt avatar Sep 14 '24 19:09 AP-Hunt