Working with Dependency Injection frameworks - jacentino/DbFun GitHub Wiki
Dependency Injection frameworks relay on objects, that can be wired-up dynamically, thus we can't define our database API-s as modules.
We must use classes with carefully designed interfaces instead.
Simplest way is defining inerfaces with query functions as properties:
type IBloggingService =
abstract member GetBlog: (int -> DbCall<Blog>)
abstract member GetPosts: (int -> DbCall<List<Post>>)
abstract member GetComments: (int -> DbCall<List<Comment>>)
type BloggingService(query: QueryBuilder) =
interface IBloggingService with
member val GetBlog = query.Sql<int, Blog>(...)
member val GetPosts = query.Sql<int, Post list>(...)
member val getComments = query.Sql<int, Comment list>(...)Of course, queries can be defined as methods, but property-based code is more concise thanks to member val construct.
In method based approach, additional fields are needed:
type IBloggingService =
abstract member GetBlog: int -> DbCall<Blog>
...
type BloggingService(query: QueryBuilder) =
let getBlog = query.Sql<int, Blog>(...)
...
interface IBloggingService with
member __.GetBlog id = getBlog id
...We must define our run function differently. Since it's impossible to pass generic functions as parameters, it should be defined as a generic method of some object:
module DB =
let createConnection () = new SqlConnection(connectionString) :> IDbConnection
let query =
let config = QueryConfig.Default createConnection
<Some configuration code>
QueryBuilder(config)
type IRunner =
abstract member Run: DbCall<'T> -> Async<'T>
let runner =
{ new IRunner with
member __.Run f = DbCall.Run(createConnection, f)
}Since query and runner are stateless objects, we can register them in a DI container as singletons:
builder.Services.AddSingleton<IBloggingService, BloggingService>()
builder.Services.AddSingleton(DB.query)
builder.Services.AddSingleton(DB.runner)Let's call our query directly from a controller:
[<ApiController>]
[<Route("[controller]")>]
type BlogController (bloggingService: IBloggingService, runner: DB.IRunner) =
inherit ControllerBase()
[<HttpGet>]
member this.Get(id: int) =
bloggingService.GetBlog(id)
|> DbCall.Map this.Ok
|> runner.Run