当我保存新的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@
属性?
答案 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
这样,所有后备字段都保持在内部,不会出现混淆。缺点是你必须两次写每个字段:首先是构造函数参数,然后是类成员。