2012-09-05 19 views
6

Mam następujących klas:MongoDB Deserializacji Z dyskryminacyjne Emisji

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

przykładem instancji to w PB:

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

Mam następnie polecenie agregacji który odfiltrowuje elementy w tablica "Widgety", która zwraca tylko te elementy, których "IsActive" jest prawdziwe. W takim przypadku polecenie właśnie zwraca cały obiekt.

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

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

Jest to klasa AggregateResult:

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

Tak, wiem, że SingleOrDefault() AsBsonDocument mogłoby to przypadku NRE, ale to nie jest problem w tej chwili.. Debugowałem kod do momentu deserializacji i zweryfikowałem, że zmienna "result" zawiera dokładnie to samo BSON, co pokazałem powyżej. Otrzymuję tę wiadomość podczas próby deserializacji wyniku: "Wystąpił błąd podczas deserializacji właściwości Widgets klasy Community.Widgets.WidgetCollection: Oczekiwano nazwy elementu jako" _v ", a nie" _id "".

Dlaczego spodziewa się, że deserializator będzie elementem "_v"?

UPDATE
Naprawiłem powyższy problem przez zmieniony typ nieruchomości widżety ICollection. Otrzymuję teraz ten błąd: Unknown discriminator value 'Widget'. Dla mnie to nie ma sensu, ponieważ dokument ma element "_t" : "Widget".

Próbowałem również wstawiania klasy pochodnej, po której wartość elementu "_t" był teraz "[ "Widget", "DerivedClass"] zgodnie z oczekiwaniami, i pojawia się ten sam błąd. Ponownie, nie dzieje się tak podczas korzystania z database.FindOneAs<>(), tylko wtedy, gdy jawnie używa się BsonSerializer.Deserialize<>(). Próbowałem dodać ten kod tuż przed wywołaniem Deserialize():

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

Ale nie jestem do końca pewien, gdzie ten kod inicjujący powinien iść, i myślałem, że to nie było potrzebne, jeśli używałem dyskryminator atrybuty na moich zajęciach.

Oto moja komenda agregacja

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; 
+1

Dlaczego nie idziesz o krok dalej i nie próbujesz samodzielnie debugować sterownika?Kod źródłowy jest tutaj: https://github.com/mongodb/mongo-csharp-driver –

+0

Dobra rozmowa. Wygląda na to, że próbuje deserializować element w tablicy, szuka łańcucha _t, a następnie łańcucha _v, który nie istnieje. Próbowałem szukać tego, co jest celem elementu _v, ale nie mogę znaleźć niczego. – anthv123

+0

Jestem trochę zdezorientowany co do tego, czego używasz w parametrze BsonSerializer.Deserialize ... Czy używasz Widget lub WidgetCollection? –

Odpowiedz

2

Nie mogę odtworzyć ten problem. Użyłem poniższego programu. Ponieważ tutaj używam stosunkowo trywialnego potoku, być może Twoim problemem jest to, że twój dokument agregacji nie powraca po mapowaniu. Czy możesz opublikować swoje polecenie agregacji?

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

Aktualizacja: Stosując nowe informacje powyżej, mam poprawione zapytanie agregacji aby działać poprawnie. Konieczne było użycie grupy w celu przywrócenia wyników do formatu oczekiwanego przez sterownik.

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

Mimo że to działa, nadal sugerowałbym, aby nie przechodzić przez ten proces i po prostu filtrować stronę klienta Widgetów.

+0

Dodałem moje polecenie do pytania. Jestem prawie pewien, że nie jest to błędne, ponieważ, jak powiedziałem, mogę debugować do AggregateResult.Result i sprawdzić wynik Json, który wraca i sprawdzić, czy zwracane są tylko Widgety z ustawionym parametrem "IsActive". – anthv123

+0

Czy rzeczywiście za każdym razem zwracasz pojedynczy dokument? $ match: {_id: "myid"}? Jeśli tak, to nie ma powodu, aby używać w tym przypadku ram agregacji ... –

+0

Tak, właśnie to robię. Powodem, dla którego chcę użyć struktury agregacji, jest filtrowanie elementów tablicy po stronie Db, a nie po stronie aplikacji. – anthv123

Powiązane problemy