简体   繁体   中英

F# type provider in DAL

I have a solution with 2 projects:

  1. F# project (for data access + other functions)
  2. C# web project (will consume functions from F#)

I will need to call SQL Server stored procedures, and am using FSharp.Data.TypeProviders to do the job.

type dbSchema = SqlDataConnection< "...", StoredProcedures=true >

let getDbContext connString = 
    match connString with
    | "" -> dbSchema.GetDataContext()
    | _ -> dbSchema.GetDataContext(connString)

Insert:

let insertItem dbContext item = 
    dbContext.CreateStoredProc(item.Stuff) 

Or

let insertItem connString item = 
    use dbContext = getDbContext connString
    dbContext.CreateStoredProc(item.Stuff)      
  1. How would I consume this from C# so that I'm not recreating the dbContext every time I do an insert?

I don't want to expose the whole dbContext , just certain stored procedures through the F# DAL.

Note: I need to pass in the connection string from web.config

Similar question here doesn't fully provide my answer: F# Type Provider for SQL in a class

  1. I am able to set getDbContext to internal or private, but not dbSchema . Any way to not expose it?

If I'm not much mistaken, the context is an instance of DataContext , which isn't thread-safe. None of the data context base classes I'm aware of, are thread-safe, and since you want to use them in a web application, you must, at the very least, create one instance per HTTP request; otherwise, the behaviour of your DAL will be defective.

On the other hand, within a single request, it may be worthwhile to reuse the same instance. Therefore, I'd go with this function design:

let insertItem dbContext item = 
    dbContext.CreateStoredProc(item.Stuff)

because that would let you create a single dbContext value associated with a single HTTP request, and reuse that for multiple database operations, because you can pass the same dbContext value to more than one function.

If you want to make all of this accessible from C#, it would be easiest on C# client developers if you wrap all the functionality in classes. Assuming the above insertItem function, as well as the getDbContext function from the OP, you could define classes like these:

type ContextShim internal(ctx) =
    member x.InsertItem(item : Item) =
        insertItem ctx item

type ContextFactory() =
    member x.CreateContext connectionString =
        let ctx = getDbContext connectionString
        ContextShim ctx

This will enable a C# client to use a Singleton instance of the ContextFactory class, and for each HTTP request, use its CreateContext method to create an instance of the ContextShim class, and then use the members on the ContextShim instance, such as InsertItem .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM