2012-09-05 18 views
6

tengo las siguientes clases:MongoDB deserialización Con Discriminador Edición

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

un ejemplo de un ejemplo de esto en el DB:

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

entonces tengo un comando de agregación que filtra los elementos en la matriz "Widgets" para devolver solo los elementos cuyo "IsActive" es verdadero. En este caso, el comando simplemente devuelve el objeto completo.

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

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

Ésta es la clase AggregateResult:

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

Sí, sé que SingleOrDefault() AsBsonDocument podría caso de que un NRE, pero ese no es el problema ahora.. He depurado el código hasta el punto de deserialización y he verificado que la variable "resultado" contiene exactamente el mismo BSON que mostré anteriormente. Recibo este mensaje cuando trato de deserializar el resultado: "Ocurrió un error al deserializar la propiedad Widgets de la clase Community.Widgets.WidgetCollection: el nombre del elemento esperado era '_v', no '_id'".

¿Por qué se espera que el deserializador sea un elemento '_v'?

ACTUALIZACIÓN
He arreglado el problema anterior cambiado el tipo de la propiedad widgets para ICollection. Ahora recibo este error: Unknown discriminator value 'Widget'. Para mí, esto no tiene sentido ya que el documento tiene el elemento "_t" : "Widget".

También intenté insertar una clase derivada, después de lo cual el valor del elemento "_t" ahora era "[ "Widget", "DerivedClass"] como esperaba, y me sale el mismo error. De nuevo, esto no ocurre cuando se usa database.FindOneAs<>(), solo cuando se usa explícitamente BsonSerializer.Deserialize<>(). He intentado añadir este código justo antes de llamar Deserialize():

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

Pero no estoy muy seguro de que en el código de inicialización debe ir, y pensé que no era necesario si yo estaba usando los atributos discriminador en mis clases.

Aquí es mi mando agregación

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

¿Por qué no da un paso más y depura el controlador usted mismo?El código fuente está aquí: https://github.com/mongodb/mongo-csharp-driver –

+0

Buena llamada. Parece que trata de deserializar el elemento en la matriz, busca la cadena _t, luego la cadena _v, que no existe. He intentado buscar cuál es el propósito del elemento _v, pero no puedo encontrar nada. – anthv123

+0

Estoy un poco confundido con respecto a lo que está usando en el parámetro de BsonSerializer.Deserialize ... ¿Está utilizando un Widget o un WidgetCollection? –

Respuesta

2

No puedo reproducir este problema. Usé el siguiente programa. Entonces, dado que estoy usando una canalización relativamente trivial aquí, tal vez su problema es que su documento de agregación no está regresando cuando se ha mapeado. ¿Podría publicar su comando de agregación?

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

Actualización: Utilizando la nueva información anterior, he corregido la consulta de agregación para que funcione correctamente. Necesitaba usar un grupo para volver a obtener los resultados en el mismo formato que esperaba el controlador.

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

Incluso teniendo en cuenta que esto funciona, todavía me gustaría sugerir a no pasar por este proceso y simplemente un filtrado del lado del cliente widgets.

+0

He agregado mi comando a la pregunta. Estoy bastante seguro de que no es erróneo porque, como dije, puedo depurar en AggregateResult.Result e inspeccionar el resultado de Json que se devuelve y verificar que solo se devuelvan Widgets con 'IsActive' establecido en verdadero. – anthv123

+0

¿Realmente solo está devolviendo un solo documento cada vez? $ match: {_id: "myid"}? Si es así, no hay ninguna razón para usar el marco de agregación en este caso ... –

+0

Sí, eso es lo que estoy haciendo. El motivo por el que deseo utilizar el marco de agregación es filtrar los elementos de la matriz en el lado Db en lugar de en el lado de la aplicación. – anthv123

Cuestiones relacionadas