F# - 类型约束。任何带有 属性 "id" 或通过字符串获取 属性 的记录

F# - Type Constraints. Any Record with property "id" or get property by string

我在实体库中有一个函数 - 基本上你可以将任何类型的记录传递给它,只要该类型具有 属性 id。

我正在使用这个,它以某种方式完美运行:

let inline Update (record: ^x) =
    let tableName = GetDatabaseTable<'x> (*E.g if record is of type User, it returns the users table *)
    let id = ((^x) : (member id : int) record)
    update {
        table tableName
        set record
        where (eq "id" id)
        excludeColumn "id"
    }
    |> db.Update

然而,当我尝试添加另一个也采用泛型类型参数的函数时,该约束停止工作:

(*
Pass in a User record of {id=1, name="Username", usergroup_id=2}
and a type argument of <order> and it should return
all orders with a "user_id" value of 1.
*)
let inline GetAllRelated<'x> (record: ^y) =
    let id = ((^y) : (member id : int) record) (*Causes an error, type constraint mismatch*)

    let tableName = GetDatabaseTable<'x>
    let columnName = nameof<'x> + "_id"

    select {
        table tableName
        where (eq columnName id)
    } |> db.Select<'x>
Type constraint mismatch when applying the default type 'obj' for a type inference variable. The type 'obj' does not support the operator 'get_id' Consider adding further type constraints

同时将 ^y 约束为一个对象。

对于未来的功能,我还希望能够动态访问记录 属性,我假设这可能通过反射实现,但不确定具体如何实现。仅使用 getProperty?

例如一个类似于

的函数
let GetRelated<'x> (record: 'y) =
 let columnToGet = nameof<'x> + "_id"
 (* 
    If you do something like 
     GetRelated<usergroup> {id=1, name="Username", usergroup_id=2}
    then that should get the property "usergroup_id" of the passed in record. 
*)
 

这东西可以做吗?我知道这有点简陋。

问题是你的函数实际上会有更多的类型参数,所以你可以指定 none 个或全部,但如果你只指定一个,它会强制其他类型参数为 obj 这就是这里发生的事情。

一个可能的解决方案是这样写:

let inline GetRelated<'x, ^y when ^y: (member id: int) > (record: 'y) = ...