Basic F # / entity framework / common functions

I am a beginner when it comes to development, so apologize in advance for my poor understanding of the terminology here ...

I am trying to create a series of very simple unrelated tables in a SQL database to get a good look around. I would like to reference them using the Entity Framework, and I would like to minimize repetition where possible. I'm trying to figure out if there is a more elegant way to do this so that I don't have to re-write getters and setters every time:

// First open "System; System.Data; System.Data.Entity; System.ComponentModel.DataAnnotations" 
// Then define "connectionString"
// Then create two simple classes that have no relation to each other, using CLIMutable 
// to help Entity Framework along.

module Model = 
    [<CLIMutable>] type Customer = {[<Key>]Id: int; FirstName: string; LastName: string;}
    [<CLIMutable>] type Item = {[<Key>]Id: int; ItemName: string; ItemDescription: string;}

module Actions =
    open Model
    type Context() =
        inherit DbContext()

        [<DefaultValue>] val mutable customers: DbSet<Customer> 
            member public this.Customers with get() = this.customers
                                           and  set v = this.customers <- v

        [<DefaultValue>] val mutable items: DbSet<Item>
            member public this.Items with get() = this.items
                                           and  set v = this.items <- v

// I would like to be able to add several more very simple classes without having to 
// repetively type out the getters and setters each time. Ideally I'd like to turn the above 
// into a generic function (method? I'm not sure of terminology) that I can call later and 
// pass it my desired parameters in terms of table/object names. However, if I move the code 
// above the Model and replace DbSet<Customer> with DbSet<'T> this won't work since it won't 
// know what type 'T is at this point. I suspect I need a generic function (method?) that 
// I can create a new instance of later and replace the generics with actual values (using 
// reflection?). Is there any way to do this without making Entity Framework get upset?

    let db = new Context()
    db.Database.Connection.ConnectionString <- Settings.connectionString

      

Likewise, I would like to create a generic function that does the Add () and SaveChanges () steps below, but I am not sure if this is possible, since I would need to dynamically replace "Clients" or "Items" with the name of the table in which i passed - and also the type "t" can change depending on what is calling the function and I don't know if that is allowed.

    let createCustomer id firstName lastName = 
        let t : Customer = {Id = id;FirstName = firstName; LastName = lastName}
        db.Customers.Add(t) |> ignore
        db.SaveChanges() |> ignore

    let createItem id itemName itemDescription = 
        let t : Item = {Id = id; ItemName = itemName; ItemDescription = itemDescription}
        db.Items.Add(t) |> ignore
        db.SaveChanges() |> ignore

open Actions
[<EntryPoint>]
let main argv =     
    createCustomer 1 "Bob" "Smith"
    createItem 1 "First item" "First item test"

    Console.ReadLine() |> ignore
    0

      

Thanks for any help, please understand, my questions are probably very simple, so if people don't need to worry about rewriting the code, but can point me in the right direction of resources for further reading, that would be great!

[edit] Many thanks to Fyodor for providing the solution. The key added an annotation like ": Context" to the "db" element when referring to it as a whole. Also, I made a silly mistake by not opening the Model module in my main argument. Updated and works as follows:

module Model = 
    [<CLIMutable>] type Customer = {[<Key>]Id: int; FirstName: string; LastName: string;}

module Actions =
    open Model
    type Context() =
        inherit DbContext()
        member val Customers: DbSet<Customer> = null with get, set

    let db = new Context()
    db.Database.Connection.ConnectionString <- Settings.connectionString

    let addAndCommit (db : Context) x =
        db.Set().Add x |> ignore
        db.SaveChanges() |> ignore

    let createCustomer id firstName lastName = 
        addAndCommit db {Id = id; FirstName = firstName; LastName = lastName}

open Actions
open Model
[<EntryPoint>]
let main argv =     
    createCustomer 1 "Bob" "Smith"
    Console.ReadLine() |> ignore
    0

      

+3


source to share


1 answer


First, to declare a property with a backing field, use member val

:

type Context() =
    ...
    member val Customers: DbSet<Customer> = null with get, set
    ...

      

Second,DbSet

you don't need a property to access a specific type. You can get it with the method DbContext.Set<'t>

:



let addAndCommit (db: Context) (x: 't) =
    db.Set<'t>().Add x |> ignore
    db.SaveChanges() |> ignore

let createCustomer id firstName lastName = 
    addAndCommit db {Id = id; FirstName = firstName; LastName = lastName}

      

But explicit annotation 't

isn't really required: F # can output it for you.

let addAndCommit (db: Context) x =
    db.Set().Add x |> ignore
    db.SaveChanges() |> ignore

      

+3


source







All Articles