基本 F# / Entity Framework / 泛型函数

Basic F# / Entity Framework / Generic Functions

在开发方面,我是一个完全的初学者,所以对于我对这里术语的不了解,提前表示歉意...

我正在尝试在 SQL 数据库中创建一些非常简单的不相关的 table 来让我的头脑清醒过来。我想使用 Entity Framework 引用这些内容,并且我希望尽可能减少重复。我正在尝试了解是否有更优雅的方法来执行此操作,这样我就不必每次都重新编写 getter 和 setter:

// 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

同样,我想创建一个执行 Add() 和 SaveChanges() 步骤的通用函数 下面,但我不确定这是否可行,因为我需要动态替换 'Customers' 或 'Items' 以及我传入的 table 名称 - 以及 't' 的类型 可能会有所不同,具体取决于调用该函数的内容,我不知道是否允许这样做。

    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

感谢您的帮助,我的问题可能很基础!

[edit] 非常感谢 Fyodor 提供解决方案。关键是在一般引用 "db" 元素时将“: Context”类型注释添加到它。此外,我犯了一个愚蠢的错误,即在我的主要论点中没有打开 "Model" 模块。更新和工作代码如下:

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

首先, 声明一个带有支持字段的 属性,使用 member val:

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

其次,访问特定类型的DbSet,不需要属性。您可以通过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}

但显式 't 注释并不是真正需要的:F# 可以为您推断。

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