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



当我保存新的 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

这样,所有支持字段都保留在内部,不会产生混淆。缺点是您必须将每个字段编写两次:首先作为构造函数参数,然后作为类成员。

最新更新