当我保存新的 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",
}
(除此之外 - "名称"与"名称"意味着我的文档中有 2 个名称列。至少我理解这个问题)。
所以我的问题是:如何在 RavenDB 中保存 F# 记录时摆脱正在创建的额外Id@
属性?
正如 Fyodor 所指出的,这是由 F# 在创建记录类型时生成支持字段的方式引起的。 RavenDB 的默认协定解析程序序列化该支持字段而不是公共属性。
您可以在 ravendb 中更改默认合约解析器。 如果你想使用Newtonsoft Json.Net,它看起来像这样:
DocumentStore.Conventions.JsonContractResolver <- new CamelCasePropertyNamesContractResolver()
这里有一个解释为什么它有效(请参阅标题为:"解释"的部分)。 简而言之,Newtonsoft 库使用该类型的公共属性,而不是私有支持字段。
我还建议,不要在Id
上拥有mutable
属性,您可以将[<CLIMutable>]
属性放在类型本身上,如下所示:
[<CLIMutable>]
type Campaign = { Id : string; name : string; description : string }
这使得库可以改变值,同时防止它在代码中。
这是...好吧,你不能完全称它们为"错误",所以让我们在 F# 编译器和 RavenDB 中都说"非直接功能"。
F# 编译器为Id
记录字段生成public
支持字段。此字段名为Id@
(所有 F# 支持字段的标准模式),它是public
,因为记录字段是可变的。对于不可变的记录字段,将internal
支持字段。为什么它需要为可变记录字段生成公共支持字段,我不知道。
现在,RavenDb 在生成架构时,显然会同时查看属性和字段。这有点不标准。通常的做法是只考虑属性。但可惜的是,Raven 选择了名为Id@
的公共字段,并使其成为架构的一部分。
您可以通过两种方式解决此问题:
首先,您可以使Id
字段不可变。我不确定这是否适用于您或 RavenDb。也许不是,因为Id
可能是在插入时生成的。
其次,可以将Campaign
声明为 true 类,而不是 F# 记录:
type Campaign( id: int, name: string, description: string ) =
member val Id = id with get, set
member val name = name
member val description = description
这样,所有支持字段都保留在内部,不会产生混淆。缺点是您必须将每个字段编写两次:首先作为构造函数参数,然后作为类成员。