1
3

I'm learning FSharp by creating a web application. I've cycled through lots of different ways of doing things, libraries, etc. Today I finally got my integration tests to use a single docker test contaner with the Expecto test framework. I couldn't find a good example online of how to do it, so I'm sharing it here.

2
1
3
2

I wrote a blog post about me getting in to FSharp web application development, but am having issues deciding how I want the data access to look. I'm very much open to feedback!

4
5
5
5

I might be going a bit all over the place, but I appreciate anyone reading and offering input.

I'm working on a side project that is an FSharp SAFE stack application. One of the issues that I've been running into is the multiple was of doing things, so I started out with some Saturn configuration, Fable.Remoting, and Giraffe endpoints and since I've upgraded to dotnet 10, I've eventually gotten rid of the Saturn components and almost exclusively am using Giraffe with dotnet EndpointRoutes so that I can better do things like e-tags and whatnot. Is this normal?

But my main example is on the data access layer. I found that there were a lot of cool looking libraries and after playing around a bit, I decided that I like Dapper, Dapper.FSharp, and DbFun.

DbFun does some cool stuff and looks like a more type safe Dapper with build or test run time evaluation of your queries, so a bad query or a typo will fail to build. It looks a bit like this (with explicit typing):

let findByUserId (userId: UserId) (queryBuilder: QueryBuilder) : (IConnector<unit> -> Async<UserOption>)->  
    queryBuilder.Sql<UserId, User option>(someSql, "id") userId  

This is pretty cool. The but though is that the new http handlers don't take an Async, they take a Task. It would be cool if it was possible to get the query object generated, but DbFun appears to be all partially applied functions, so I can't put it through a runner that returns a Task.

Now, I can convert the Async to a Task, but I'd prefer not to switch between the two. I ran some tests between DbFun with Async converted to Task and Dapper and there was a difference in memory and execution time. It's not a huge deal and this is a side project, but I'd prefer to stick with one type up through the stack.

This currently leaves me with using Dapper and the typical Repository pattern, though even this repo is getting bloated already.

What I'm doing at the moment until I decide a direction is I'm creating the DbFun queries for testing and type safety stuff, but also putting those queries into my dapper commands wrapped with instrumentation:

// I'm pretty sure I can make some handlers for dapper to work with my single case DUs, but I'm still looking at the best way to do strong typing of IDs.  
member this.FindTask(id:IdentityId, mediaId: MediaId, ct: CancellationToken) : Task<SomeFindResponseType option> =  
            let instrument = withDbActivity logger (nameof(findByUserIdAndMediaIdQuery)) (Some findByUserIdAndMediaIdSql)  
            instrument(fun () -> task {  
                let conn = connectionFactory()  
                let mediaGuid: Guid = mediaId  
                let guidValue = match id with | IdentityId guid -> guid  
                let! result = conn.QuerySingleOrDefaultAsync< SomeFindResponseType >(  
                    CommandDefinition(findByUserIdAndMediaIdSql, {| id = guidValue.ToString(); mediaId = mediaGuid |}, cancellationToken = ct))  
                return Option.ofObj result  
            })  

What is normally done with this sort of thing. Does anyone have any recommendations? Do most people go with the convenience of the FSharp libs and convert Async to Task and take the hit? Or do you just stick with dapper and maybe make a lightweight query/runner object to make things more functional?

6
5
7
3
8
2
9
3
10
2
11
7
12
5
13
2
14
3
15
0
16
3
17
3
18
6
19
8
20
6
21
4
22
5
23
2
24
5
25
3
view more: next ›

F Sharp

210 readers
3 users here now

founded 2 years ago
MODERATORS