6

次のクラスがあります。

[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; }
}

DB でのこれのインスタンスの例:

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

次に、"Widgets" 配列内の要素を除外して、"IsActive" が true である要素のみを返す集約コマンドを用意しました。この場合、コマンドはオブジェクト全体を返すだけです。

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 のケースになる可能性があることは知っていますが、それは今のところ問題ではありません。逆シリアル化のポイントまでコードをデバッグし、「result」変数に上記とまったく同じ BSON が含まれていることを確認しました。結果を逆シリアル化しようとすると、次のメッセージが表示されます。

デシリアライザーに '_v' 要素が必要なのはなぜですか?

更新
Widgets プロパティのタイプを に変更して、上記の問題を修正しましたICollection。私は今、このエラーを受け取っています:Unknown discriminator value 'Widget'.ドキュメントに"_t" : "Widget"要素があるので、これは意味がありません。

また、派生クラスを挿入しようとしましたが、その後、「_t」要素の値が"[ "Widget", "DerivedClass"]期待どおりになり、同じエラーが発生しました。繰り返しますが、これは を使用している場合には発生せずdatabase.FindOneAs<>()、明示的に を使用している場合にのみ発生しBsonSerializer.Deserialize<>()ます。呼び出す直前にこのコードを追加してみました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;
4

1 に答える 1

2

この問題を再現することはできません。以下のプログラムを使用しました。したがって、ここでは比較的些細なパイプラインを使用しているので、おそらく問題は、マッピングしたときに集計ドキュメントが返されないことです。集計コマンドを投稿していただけますか?

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")))))})));

これが機能する場合でも、このプロセスを実行せずに、ウィジェットのクライアント側を単純にフィルタリングすることをお勧めします。

于 2012-09-11T17:18:19.507 に答える