使用 F# 添加额外的 Id 列在 RavenDb 中保存记录
Saving record in RavenDb with F# adding extra Id column
当我保存一个新的 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 Whosebug!"
}
现在,当我在 C# 中使用同一个数据库(和文档)时,我没有获得额外的 Id@
值。这是我在 C# 中保存记录时的样子:
{
"Description": "Hello Whosebug!",
"Name": "Look this worked fine",
}
(另外 - "name" vs "Name" 意味着我的文档中有 2 个名称列。我至少理解这个问题)。
所以我的问题是:如何摆脱在 RavenDb 中保存 F# 记录时创建的额外 Id@
属性?
这是...的组合...好吧,你不能完全称它们为 "bugs",所以让我们在 F# 编译器和 RavenDb 中都称其为 "non-straightforward features"。
F# 编译器为 Id
记录字段生成一个 public
支持字段。此字段名为 Id@
(所有 F# 支持字段的标准模式),并且它是 public
,因为记录字段是可变的。对于不可变记录字段,支持字段将为 internal
。为什么它需要为可变记录字段生成一个 public 支持字段,我不知道。
现在,RavenDb 在生成模式时,显然会同时查看属性 和 字段。这有点不标准。通常的做法是只考虑属性。但是,遗憾的是,Raven 选择了名为 Id@
的 public 字段,并将其作为模式的一部分。
您可以通过两种方式解决这个问题:
首先,您可以使 Id
字段不可变。我不确定这是否适合您或 RavenDb。也许不是,因为 Id
可能是在插入时生成的。
其次,您可以声明您的 Campaign
不是 F# 记录,而是真正的 class:
type Campaign( id: int, name: string, description: string ) =
member val Id = id with get, set
member val name = name
member val description = description
这样,所有支持字段都保留在内部,不会出现混淆。缺点是您必须将每个字段写两次:首先作为构造函数参数,然后作为 class 成员。
正如 Fyodor 所指出的,这是由 F# 在您创建记录类型时生成支持字段的方式引起的。 RavenDB 的默认合同解析器序列化该支持字段而不是 public 属性.
You can change the default contract resolver in ravendb. 如果你想使用 Newtonsoft Json.Net:
它看起来像这样
DocumentStore.Conventions.JsonContractResolver <- new CamelCasePropertyNamesContractResolver()
这里有一个解释为什么这有效 here(请参阅标题为:"The explanation" 的部分)。简而言之,Newtonsoft 库使用类型的 public 属性而不是私有支持字段。
我还建议,不要在 Id
上使用 mutable
属性,您可以将 [<CLIMutable>]
属性放在类型本身上,例如:
[<CLIMutable>]
type Campaign = { Id : string; name : string; description : string }
这使得库可以改变值,同时防止它出现在您的代码中。
当我保存一个新的 F# 记录时,我在 RavenDb 文档中得到一个名为 Id@
的额外列,当我在代码中加载或查看对象时它会显示;它甚至通过我的 F# API.
这是我的 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 Whosebug!"
}
现在,当我在 C# 中使用同一个数据库(和文档)时,我没有获得额外的 Id@
值。这是我在 C# 中保存记录时的样子:
{
"Description": "Hello Whosebug!",
"Name": "Look this worked fine",
}
(另外 - "name" vs "Name" 意味着我的文档中有 2 个名称列。我至少理解这个问题)。
所以我的问题是:如何摆脱在 RavenDb 中保存 F# 记录时创建的额外 Id@
属性?
这是...的组合...好吧,你不能完全称它们为 "bugs",所以让我们在 F# 编译器和 RavenDb 中都称其为 "non-straightforward features"。
F# 编译器为 Id
记录字段生成一个 public
支持字段。此字段名为 Id@
(所有 F# 支持字段的标准模式),并且它是 public
,因为记录字段是可变的。对于不可变记录字段,支持字段将为 internal
。为什么它需要为可变记录字段生成一个 public 支持字段,我不知道。
现在,RavenDb 在生成模式时,显然会同时查看属性 和 字段。这有点不标准。通常的做法是只考虑属性。但是,遗憾的是,Raven 选择了名为 Id@
的 public 字段,并将其作为模式的一部分。
您可以通过两种方式解决这个问题:
首先,您可以使 Id
字段不可变。我不确定这是否适合您或 RavenDb。也许不是,因为 Id
可能是在插入时生成的。
其次,您可以声明您的 Campaign
不是 F# 记录,而是真正的 class:
type Campaign( id: int, name: string, description: string ) =
member val Id = id with get, set
member val name = name
member val description = description
这样,所有支持字段都保留在内部,不会出现混淆。缺点是您必须将每个字段写两次:首先作为构造函数参数,然后作为 class 成员。
正如 Fyodor 所指出的,这是由 F# 在您创建记录类型时生成支持字段的方式引起的。 RavenDB 的默认合同解析器序列化该支持字段而不是 public 属性.
You can change the default contract resolver in ravendb. 如果你想使用 Newtonsoft Json.Net:
它看起来像这样DocumentStore.Conventions.JsonContractResolver <- new CamelCasePropertyNamesContractResolver()
这里有一个解释为什么这有效 here(请参阅标题为:"The explanation" 的部分)。简而言之,Newtonsoft 库使用类型的 public 属性而不是私有支持字段。
我还建议,不要在 Id
上使用 mutable
属性,您可以将 [<CLIMutable>]
属性放在类型本身上,例如:
[<CLIMutable>]
type Campaign = { Id : string; name : string; description : string }
这使得库可以改变值,同时防止它出现在您的代码中。