【问题标题】:MongoDB Deserialization With Discriminator Issue带有鉴别器问题的 MongoDB 反序列化
【发布时间】:2012-09-13 02:38:31
【问题描述】:

我有以下课程:

[BsonIgnoreExtraElements]
public class WidgetCollection
{
    [BsonId]
    public int AccountId { get; set; }
    public ReadOnlyCollection<Widget> Widgets { get; set; }
}


[BsonIgnoreExtraElements]
[BsonDiscriminator(RootClass = true)]
[BsonKnownTypes(typeof(OtherObject1), ...)]
public class Widget
{
    public ObjectId Id { get; set; }
    public string Title { get; set; }
    public int Position { get; set; }
    public WidgetKind Kind { get; set; }
    public bool IsActive { get; set; }
}

数据库中 this 实例的示例:

{ "_id" : 2993644, "Widgets" : [        {       "_t" : "Widget",        "_id" : ObjectId("504797b327e10b1e80c838ac"),   "Title" : "My Notes",   "Position" : 1,         "Kind" : 0,     "IsActive" : true } ] }

然后我有一个聚合命令,它过滤掉“Widgets”数组中的元素,只返回“IsActive”为真的元素。在这种情况下,该命令只返回整个对象。

var command = new CommandDocument
                        {
                            {"aggregate", "myCollection" },
                            {"pipeline", commandArray }
                        };

        var result = database.RunCommandAs<AggregateResult<WidgetCollection>>(command).Result;
        return result;

这是 AggregateResult 类:

public class AggregateResult<T> : CommandResult
{
    public T Result
    {
        get
        {
           var result = Response["result"].AsBsonArray.SingleOrDefault().AsBsonDocument;
           return BsonSerializer.Deserialize<T>(result);
         }
     }
 }

是的,我知道 SingleOrDefault().AsBsonDocument 可以处理 NRE,但这不是现在的问题。我已将代码调试到反序列化点,并验证“结果”变量包含与上面显示的完全相同的 BSON。我在尝试反序列化结果时收到此消息:“反序列化 Community.Widgets.WidgetCollection 类的 Widgets 属性时发生错误:预期元素名称为 '_v',而不是 '_id'”。

为什么反序列化器需要一个 '_v' 元素?

更新
我通过将 Widgets 属性的类型更改为 ICollection 来解决上述问题。我现在收到此错误:Unknown discriminator value 'Widget'. 对我来说,这是没有意义的,因为文档有 "_t" : "Widget" 元素。

我还尝试插入一个派生类,之后“_t”元素的值现在是"[ "Widget", "DerivedClass"],正如预期的那样,我得到了同样的错误。同样,在使用 database.FindOneAs&lt;&gt;() 时不会发生这种情况,只有在显式使用 BsonSerializer.Deserialize&lt;&gt;() 时才会发生这种情况。我尝试在调用 Deserialize() 之前添加此代码:

BsonClassMap.RegisterClassMap<Widget>(cm => { cm.AutoMap(); cm.SetIsRootClass(true); });
BsonClassMap.RegisterClassMap<RssFeedWidget>();

但我不确定初始化代码应该放在哪里,而且我认为如果我在我的类上使用鉴别器属性,则不需要它。

这是我的聚合命令

BsonDocument documentMatch = new BsonDocument{{"$match", new BsonDocument{{"_id", documentId}}}};
BsonDocument unwindArray = new BsonDocument{{"$unwind", "$Widgets"}};
BsonDocument arrayMatch = new BsonDocument{{"$match", new BsonDocument{{"Widgets.IsActive", true}}}});

BsonArray commandArray = new BsonArray();
commandArray.Add(documentMatch);
commandArray.Add(unwindArray),
commandArray.Add(arrayMatch);
var command = new CommandDocument
                  {
                      {"aggregate", collectionName},
                      {"pipeline", commandArray}
                  }

var result = database.RunCommandAs<AggregateResult<WidgetCollection>>(command).Result;

【问题讨论】:

  • 何不更进一步,自己调试驱动呢?源代码就在这里:github.com/mongodb/mongo-csharp-driver
  • 好电话。看起来当它试图反序列化数组中的元素时,它会查找 _t 字符串,然后查找不存在的 _v 字符串。我已经尝试搜索 _v 元素的目的是什么,但我找不到任何东西。
  • 我对您在 BsonSerializer.Deserialize 的 参数中使用的内容有点困惑...您使用的是 Widget 还是 WidgetCollection?
  • 抱歉,我已经更新了代码。它应该是 WidgetCollection。
  • 工作中,没有忘记...

标签: c# mongodb deserialization


【解决方案1】:

我无法重现此问题。我使用了以下程序。因此,由于我在这里使用了一个相对简单的管道,所以您的问题可能是您的聚合文档在映射后没有返回。你能发布你的聚合命令吗?

using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace TestConsole_Source
{
    class Program
    {
        [BsonIgnoreExtraElements]
        public class WidgetCollection
        {
            [BsonId]
            public int AccountId { get; set; }
            public ReadOnlyCollection<Widget> Widgets { get; set; }
        }


        [BsonIgnoreExtraElements]
        [BsonDiscriminator(RootClass = true)]
        public class Widget
        {
            public string Title { get; set; }
            public int Position { get; set; }
            public bool IsActive { get; set; }
        }

        static void Main(string[] args)
        {
            var server = MongoServer.Create();
            server.Connect();

            var db = server.GetDatabase("widgettest");
            var collection = db.GetCollection<WidgetCollection>("widgets");
            collection.Drop();

            var widgets = new WidgetCollection();
            var widget1 = new Widget { Title = "One", Position = 0, IsActive = true };
            var widget2 = new Widget { Title = "Two", Position = 1, IsActive = true };
            var widget3 = new Widget { Title = "Three", Position = 2, IsActive = false };
            widgets.Widgets = new List<Widget> { widget1, widget2, widget3 }.AsReadOnly();

            collection.Save(widgets);

            var command = new CommandDocument(
                new BsonElement("aggregate", "widgets"),
                new BsonElement("pipeline", new BsonArray(new [] {
                    new BsonDocument(
                        new BsonElement("$project", new BsonDocument("Widgets", 1)))})));

            var response = db.RunCommand(command);
            var rawDoc = response.Response["result"].AsBsonArray.SingleOrDefault().AsBsonDocument;

            var doc = BsonSerializer.Deserialize<WidgetCollection>(rawDoc);

            //Console.ReadKey();
        }
    }
}

更新: 使用上面的新信息,我已更正您的聚合查询以正常工作。您需要使用组才能将结果恢复为与驱动程序期望的格式相同的格式。

    var command = new CommandDocument(
        new BsonElement("aggregate", "widgets"),
        new BsonElement("pipeline", new BsonArray(new[] {
            new BsonDocument(
                new BsonElement("$unwind", "$Widgets")),
            new BsonDocument(
                new BsonElement("$match", new BsonDocument("Widgets.IsActive", true))),
            new BsonDocument(
                new BsonElement("$group", new BsonDocument(
                    new BsonElement("_id", "$_id"),
                    new BsonElement("Widgets", new BsonDocument("$addToSet", "$Widgets")))))})));

即使这样可行,我仍然建议不要执行此过程并简单地过滤小部件客户端。

【讨论】:

  • 我已将我的命令添加到问题中。我很确定这没有错误,因为就像我说的那样,我可以调试 AggregateResult.Result 并检查返回的 Json 结果并验证只返回 'IsActive' 设置为 true 的小部件。
  • 您真的每次都只返回一个文档吗? $match : { _id : "myid" }?如果是这样,在这种情况下没有理由使用聚合框架......
  • 是的,这就是我正在做的事情。我想使用聚合框架的原因是在 Db 端而不是在应用程序端过滤数组项。
  • 很可能不超过 10-15。
  • 那么我真的不会担心这样做。你应该把它们拉回来并在客户端过滤它们。
猜你喜欢
  • 2016-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多