使用F#在RavenDb中保存记录,添加额外的Id列

时间:2017-04-14 15:31:55

标签: f# ravendb

当我保存新的F#记录时,我在RavenDb文档中获得了一个名为Id@的额外列,当我在代码中加载或查看对象时,它会显示出来;它甚至通过我的F#API转换为JSON。

这是我的F#记录类型:

type Campaign = { mutable Id : string; name : string; description : string }

我没有做任何令人兴奋的事情来保存它:

let save c : Campaign =
    use session = store.OpenSession()
    session.Store(c)
    session.SaveChanges()
    c

保存记录的新实例会创建ID为campaigns/289的文档。以下是RavenDb中文档的完整值:

{
    "Id@": "campaigns/289",
    "name": "Recreating Id bug",
    "description": "Hello StackOverflow!"
}

现在,当我在C#中使用相同的数据库(和文档)时,我没有获得额外的Id@值。这是我在C#中保存时的记录:

{
    "Description": "Hello StackOverflow!",
    "Name": "Look this worked fine",
}

(旁白 - "名称" vs"名称"表示我的文档中有2个名称列。至少我理解这个问题。)

所以我的问题是:如何在RavenDb中保存F#记录时除去正在创建的额外Id@属性?

2 个答案:

答案 0 :(得分:4)

正如Fyodor所指出的,这是由于F#在创建记录类型时如何生成支持字段。 RavenDB的默认合约解析程序序列化该支持字段而不是公共属性。

You can change the default contract resolver in ravendb.如果你想使用Newtonsoft Json.Net,它会看起来像这样:

DocumentStore.Conventions.JsonContractResolver <- new CamelCasePropertyNamesContractResolver()

有一个解释为什么这有效here(参见标题为“解释”的部分)。简而言之,Newtonsoft库使用该类型的公共属性而不是私有支持字段。

我还建议您不要在mutable上使用Id属性,而是可以将[<CLIMutable>]属性放在类型本身上,如:

[<CLIMutable>]
type Campaign = { Id : string; name : string; description : string }

这使得库可以改变值,同时在代码中阻止它。

答案 1 :(得分:3)

这是......好吧,你不能把它们称为“错误”,所以让我们说F#编译器和RavenDb中的“非直接功能”。

F#编译器为public记录字段生成Id支持字段。此字段名为Id@(所有F#支持字段的标准模式),它是public,因为记录字段是可变的。对于不可变记录字段,后备字段为internal。为什么它需要为可变记录字段生成公共支持字段,我不知道。

现在,RavenDb在生成架构时,显然会查看属性字段。这有点不标准。通常的做法是只考虑属性。但是,唉,Raven选择了名为Id@的公共字段,并将其作为模式的一部分。

您可以通过两种方式解决此问题:

首先,您可以使Id字段不可变。我不确定这对你或RavenDb是否有效。也许不是,因为Id可能是在插入时生成的。

第二次,您可以声明Campaign不是F#记录,而是真正的类:

type Campaign( id: int, name: string, description: string ) = 
    member val Id = id with get, set
    member val name = name
    member val description = description

这样,所有后备字段都保持在内部,不会出现混淆。缺点是你必须两次写每个字段:首先是构造函数参数,然后是类成员。