【问题标题】:Strange behavior of MongoDB LINQ provider for fields called "id"MongoDB LINQ 提供程序对名为“id”的字段的奇怪行为
【发布时间】:2012-12-01 07:26:52
【问题描述】:

这是 Mongo LINQ 提供程序失败的 JSON 文档:

{"results":
     {"text":"@twitterapi  http://tinyurl.com/ctrefg",
     "to_user_id":396524,
     "to_user":"TwitterAPI",
     "from_user":"jkoum",
     "metadata":
     {
      "result_type":"popular",
      "recent_retweets": 109
     },
     "id":1478555574, 
     "from_user_id":1833773,
     "iso_language_code":"nl",
     "source":"<a href=\"http://twitter.com/\">twitter<\/a>",
     "profile_image_url":"http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg",
     "created_at":"Wed, 08 Apr 2009 19:22:10 +0000",
     "since_id":0,
     "max_id":1480307926,
     "refresh_url":"?since_id=1480307926&q=%40twitterapi",
     "results_per_page":15,
     "next_page":"?page=2&max_id=1480307926&q=%40twitterapi",
     "completed_in":0.031704,
     "page":1,
     "query":"%40twitterapi"}
}

注意一个“id”字段。以下是相关的 C# 实体定义:

class Twitter
{
    [BsonId]
    public ObjectId Id { get; set; }
    public Result results { get; set; }
}

private class Result
{
    public string text { get; set; }
    public int to_user_id { get; set; }
    public string to_user { get; set; }
    public string from_user { get; set; }
    public Metadata metadata { get; set; }
    public int id { get; set; }
    public int from_user_id { get; set; }
    public string iso_language_code { get; set; }
    public string source { get; set; }
    public string profile_image_url { get; set; }
    public string created_at { get; set; }
    public int since_id { get; set; }
    public int max_id { get; set; }
    public string refresh_url { get; set; }
    public int results_per_page { get; set; }
    public string next_page { get; set; }
    public double completed_in { get; set; }
    public int page { get; set; }
    public string query { get; set; }
}

class Metadata
{
    public string result_type { get; set; }
    public int recent_retweets { get; set; }
}

如果我创建一个“Twitter”集合并保存上面的文档,那么当我使用 Mongo LINQ 提供程序查询它时,它会引发 FileFormatException 异常: "元素 'id' 不匹配 Mongo.Context.Tests.NativeTests+Result 类的任何字段或属性"

但是,有两种替代解决方法可以解决此问题:

  1. 重命名结果“id”字段,例如在 JSON doc 和 Result 类中都“idd”。然后 LINQ 查询工作。
  2. 保留“id”字段,但另外将字段“Id”添加到 Result 类并用属性 [BsonId] 标记它。现在 Result 类同时包含“Id”和“id”字段,但查询有效!

我使用 Mongo API 来查询集合,一切正常,所以我想这一定是 MongoDB LINQ 提供程序中的错误。嵌套 JSON 元素中的“id”不应该是保留的工作,不是吗?

更新:这是本机 API 查询执行的结果:

> db.Twitter.find().limit(1);

{ "_id" : ObjectId("50c9d3a4870f4f17e049332b"),
 "results" : { 
    "text" : "@twitterapi  http://tinyurl.com/ctrefg", 
    "to_user_id" : 396524, 
    "to_user" : "TwitterAPI", 
    "from_user" : "jkoum", 
    "metadata" : { "result_type" : "popular", "recent_retweets" : 109 }, 
    "id" : 1478555574, 
    "from_user_id" : 1833773, "
    iso_language_code" : "nl", 
    "source" : "<a href=\"http://twitter.com/\">twitter</a>", 
    "profile_image_url" : "http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg", 
    "created_at" : "Wed, 08 Apr 2009 19:22:10 +0000", 
    "since_id" : 0, 
    "max_id" : 1480307926, 
    "refresh_url" : "?since_id=1480307926&q=%40twitterapi", "results_per_page" : 15,    "next_page" : "?page=2&max_id=1480307926&q=%40twitterapi", 
    "completed_in" : 0.031704, 
    "page" : 1, 
    "query" : "%40twitterapi" 
    } 
}

【问题讨论】:

  • +1 解决方法 - 谢谢。有趣的是,似乎只有在包含的单个实例对象具有 id 字段时才会引发错误。带有idList 子元素没有问题。

标签: linq mongodb mongodb-.net-driver


【解决方案1】:

问题是您的 POCO 与您的 JSON 不匹配

您的 Twitter 类有一个 ID 和一个名为 results
然而,您的 JSON 只是有结果。这就是问题所在。

所以实际上,您绕过了Twitter 类,只是创建了Result 的一个实例

您的 JSON 应该类似于:

{
  _id: ObjectId("50c9c8f3e0ae76405f7d2b5e"), 
  "results": { 
     "text":"@twitterapi  http://tinyurl.com/ctrefg",
     "to_user_id":396524,
     "to_user":"TwitterAPI",
     "from_user":"jkoum",
     "metadata":
     {
      "result_type":"popular",
      "recent_retweets": 109
     },
     "id":1478555574, 
     "from_user_id":1833773,
     "iso_language_code":"nl",
     "source":"<a href=\"http://twitter.com/\">twitter<\/a>",
     "profile_image_url":"http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg",
     "created_at":"Wed, 08 Apr 2009 19:22:10 +0000",
     "since_id":0,
     "max_id":1480307926,
     "refresh_url":"?since_id=1480307926&q=%40twitterapi",
     "results_per_page":15,
     "next_page":"?page=2&max_id=1480307926&q=%40twitterapi",
     "completed_in":0.031704,
     "page":1,
     "query":"%40twitterapi"}
    }
}

编辑

您的results 实际上是一个嵌入文档(目前是您的 POCO 模型),因此您还应该使用 [BsonId] 标记 Result.ID

【讨论】:

  • 谢谢亚历克斯,我起初以为你解释了行为,但后来我查看了 MongoDB 的内容。忘记原始的 JSON:它是用来插入数据的。现在数据被插入并且文档有它的 ObjectId。因此,当我查询集合时,文档的 Id 已正确映射到 Twitter.Id,并且文档中不再有 Mongo ID,因此嵌套元素应该不需要映射任何内容。
  • 你能澄清你所说的“所以嵌套元素不需要映射任何东西”是什么意思 - 你能发布(例如)db.collection.find().limit( 1)
  • 当然,我刚刚使用本地 Mongo API 调用的结果更新了原始帖子。如您所见,ObjectId 附加到外部文档,而不是嵌套的“结果”元素。此外,我可以有多个嵌套元素,它们都没有对应的 ID。
  • 这不是我的问题;我的问题与上面选择的解决方案中提到的完全一样:stackoverflow.com/a/13869920/980423
【解决方案2】:

MongoDB 要求存储在数据库中的每个文档都有一个名为“_id”的字段(在根级别)。

C# 驱动程序假定您的类中称为“Id”、“id”或“_id”的任何字段都将映射到特殊的“_id”字段。这是一个约定,可以被覆盖。 C# 驱动程序不知道您的 Result 类不打算用作集合的根文档,因此它会找到您的“id”字段并将其映射到数据库中的“_id”。

您可以覆盖它的一种方法是更改​​类中字段的名称(如您所见)。然后您还可以使用 [BsonElement] 属性将您的 C# 字段名称(例如“idd”)映射到数据库中实际使用的任何名称(例如“id”)。例如:

public class Result
{
    [BsonElement("id")]
    public int idd; // matches "id" in the database
    // other fields
}

另一种替代方法是覆盖查找类的“Id”成员的约定,以抑制 Result 类的 C# 驱动程序的默认行为。您可以通过为您的 Result 类注册一个新的 ConventionProfile 来做到这一点。例如:

var noIdConventions= new ConventionProfile();
noIdConventions.SetIdMemberConvention(new NamedIdMemberConvention()); // no names
BsonClassMap.RegisterConventions(noIdConventions, t => t == typeof(Result));

您必须确保在您的 Result 类被映射之前,在程序的早期就这样做。

【讨论】:

  • 我已经为这个问题创建了一个 JIRA 票,因为当你不希望这种情况发生时,似乎应该有一种简单的方法来覆盖 id 到 _id 的映射。见:jira.mongodb.org/browse/CSHARP-648
  • 感谢您的澄清。不,我知道它在内部是如何工作的。在 JIRA 票证中,我喜欢将 BsonNoId 属性用于嵌套文档的替代方案。易于掌握和使用。如果我们考虑约定优于配置,您是否认为默认情况下可以禁用嵌套类的映射,即如果一个类在另一个类中定义并且外部类具有 Id 或 [BsonId] 标记的字段?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-26
  • 1970-01-01
相关资源
最近更新 更多