2015-06-12 18 views
5

Sto tentando di selezionare l'ultimo record in una collezione per ogni gruppo secondo una chiave multi-campo, utilizzando l'interfaccia aggregata fluente:Mongo C# driver 2.0 Aggregate Gruppo eccezione

 var matches = await Collection.Aggregate() 
      .Match(x => x.EffectiveDate >= minEffectiveDate) 
      .SortByDescending(x => x.LastUpdate) 
      .Group(key => new { key.EffectiveDate, key.ProductOid, key.InstrumentParentOid, key.ComponentOid, key.EventSummary }, g => g.First()) 
      .ToListAsync(); 

Tuttavia, ottengo il seguente eccezione:

System.InvalidCastException occurred 
    HResult=-2147467262 
    Message=Unable to cast object of type 'MongoDB.Driver.Linq.Expressions.SerializationExpression' to type 'System.Linq.Expressions.MethodCallExpression'. 
    Source=MongoDB.Driver 
    StackTrace: 
    at MongoDB.Driver.Linq.Processors.GroupSerializationInfoBinder.GetBodyFromSelector(MethodCallExpression node) 
    at MongoDB.Driver.Linq.Processors.GroupSerializationInfoBinder.GetAggregationArgument(MethodCallExpression node) 
    at MongoDB.Driver.Linq.Processors.GroupSerializationInfoBinder.VisitMethodCall(MethodCallExpression node) 
    at MongoDB.Driver.Linq.Translators.AggregateProjectionTranslator.BindSerializationInfo(SerializationInfoBinder binder, LambdaExpression node, IBsonSerializer parameterSerializer) 
    at MongoDB.Driver.Linq.Translators.AggregateProjectionTranslator.TranslateGroup[TKey,TDocument,TResult](Expression`1 idProjector, Expression`1 groupProjector, IBsonSerializer`1 parameterSerializer, IBsonSerializerRegistry serializerRegistry) 
    at MongoDB.Driver.IAggregateFluentExtensions.GroupExpressionProjection`3.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry) 
    at MongoDB.Driver.AggregateFluent`2.<>c__DisplayClass1`1.<Group>b__0(IBsonSerializer`1 s, IBsonSerializerRegistry sr) 
    at MongoDB.Driver.DelegatedPipelineStageDefinition`2.Render(IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry) 
    at MongoDB.Driver.PipelineStageDefinition`2.MongoDB.Driver.IPipelineStageDefinition.Render(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry) 
    at MongoDB.Driver.PipelineStagePipelineDefinition`2.Render(IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry) 
    at MongoDB.Driver.MongoCollectionImpl`1.<AggregateAsync>d__7`1.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at MongoDB.Driver.IAsyncCursorSourceExtensions.<ToListAsync>d__14`1.MoveNext() 

Ok. Quindi non mi piace il metodo di estensione IEnumerable per qualche motivo. Così provo estrarre il metodo di estensione LINQ:

 var matches = await Collection.Aggregate() 
      .Match(x => x.EffectiveDate >= minEffectiveDate) 
      .SortByDescending(x => x.LastUpdate) 
      .Group(key => new { key.EffectiveDate, key.ProductOid, key.InstrumentParentOid, key.ComponentOid, key.EventSummary }, g => g) 
      .ToListAsync(); 
     return matches.Select(x => x.First()); 

Ma:

System.InvalidCastException occurred 
    HResult=-2147467262 
    Message=Unable to cast object of type 'MongoDB.Bson.Serialization.Serializers.ArraySerializer`1[SPMO.Providers.Audit.Messages.ProductAdjustmentAuditDataDb]' to type 'MongoDB.Bson.Serialization.IBsonSerializer`1[System.Linq.IGrouping`2[<>f__AnonymousType0`5[System.Nullable`1[System.DateTime],System.Nullable`1[System.Int32],System.Nullable`1[System.Int32],System.Nullable`1[System.Int32],System.String],SPMO.Providers.Audit.Messages.ProductAdjustmentAuditDataDb]]'. 
    Source=MongoDB.Driver 
    StackTrace: 
     at MongoDB.Driver.Linq.Translators.AggregateProjectionTranslator.TranslateGroup[TKey,TDocument,TResult](Expression`1 idProjector, Expression`1 groupProjector, IBsonSerializer`1 parameterSerializer, IBsonSerializerRegistry serializerRegistry) 
     at MongoDB.Driver.IAggregateFluentExtensions.GroupExpressionProjection`3.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry) 
     at MongoDB.Driver.AggregateFluent`2.<>c__DisplayClass1`1.<Group>b__0(IBsonSerializer`1 s, IBsonSerializerRegistry sr) 
     at MongoDB.Driver.DelegatedPipelineStageDefinition`2.Render(IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry) 
     at MongoDB.Driver.PipelineStageDefinition`2.MongoDB.Driver.IPipelineStageDefinition.Render(IBsonSerializer inputSerializer, IBsonSerializerRegistry serializerRegistry) 
     at MongoDB.Driver.PipelineStagePipelineDefinition`2.Render(IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry) 
     at MongoDB.Driver.MongoCollectionImpl`1.<AggregateAsync>d__7`1.MoveNext() 
    --- End of stack trace from previous location where exception was thrown --- 
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
     at MongoDB.Driver.IAsyncCursorSourceExtensions.<ToListAsync>d__14`1.MoveNext() 
    --- End of stack trace from previous location where exception was thrown --- 
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
     at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() 

Il mio piano B è solo di dimenticare aggregato, fare un semplice find() e poi fare un GroupBy() in vaniglia LINQ, ma mi piacerebbe farlo funzionare sul DB in quanto sarà più efficiente.

+1

Hai provato un tipo di nome al posto del tipo anonimo? – lobsterism

+0

Grazie per il suggerimento. Ho effettuato il cross-post al gruppo di utenti di Google C# di MongoDB qui: https://groups.google.com/forum/#!topic/mongodb-csharp/nmSYSiyBzOo: sembra che fare .First() sull'intero oggetto non sia supportato ; devi definire una proiezione. Sto andando con il mio piano B per ora. – ultra909

risposta

4

Ho riscontrato lo stesso problema. La mia soluzione alternativa è diversa: anziché utilizzare il lato client LINQ GroupBy(), effettuo due chiamate al server.

  1. Ottenere l'oggetto MongoDB dei documenti che desidero recuperare, ovvero i documenti che vorrei che il gruppo() emettesse semplicemente per me come POCO.
  2. fare una ricerca() con Contiene() contro la raccolta di objectIds dal punto 1.

codice che dimostra questa

public static class WidgetWorker 
{ 
    public static void Main() 
    { 
     //setup 
     var widgetCollection = new MongoClient("mongodb://localhost:27017") 
      .GetDatabase("WidgetDatabase") 
      .GetCollection<Widget>("Widget"); 
     widgetCollection.DeleteManyAsync(x => true).Wait(); //remove all existing rows 

     //create widgets and add to DB; 2 SKUs, each with multiple revisions 
     var widgetA1 = new Widget() { SKU = "aaaa", Revision = 1M, Cost = 10 }; 
     var widgetA2 = new Widget() { SKU = "aaaa", Revision = 2.1M, Cost = 20 }; 
     var widgetA3 = new Widget() { SKU = "aaaa", Revision = 2.2M, Cost = 30 }; 
     var widgetB1 = new Widget() { SKU = "bbbb", Revision = 1M, Cost = 40 }; 
     var widgetB2 = new Widget() { SKU = "bbbb", Revision = 1.1M, Cost = 50 }; 
     widgetCollection.InsertManyAsync(new[] { widgetA1, widgetA2, widgetA3, widgetB1, widgetB2 }).Wait(); 

     //get the ObjectId of the most Recent revision of each SKU 
     var r = widgetCollection 
      .Aggregate() 
      .SortByDescending(x => x.Revision) 
      .Group(x => x.SKU, g => new { Id = g.First().Id }) 
      .ToListAsync() 
      .Result; 

     //get the Widget objects for the list of ids just collected 
     var ids = r.Select(x => x.Id).ToArray(); 
     var widgets = widgetCollection 
      .Find(x => ids.Contains(x.Id)) 
      .ToListAsync() 
      .Result; 

     //check results 
     Debug.Assert(widgets.Count() == 2); 
     Debug.Assert(widgets.Single(x => x.SKU == "aaaa").Revision == 2.2M); 
     Debug.Assert(widgets.Single(x => x.SKU == "bbbb").Revision == 1.1M); 
    } 
} 

public class Widget 
{ 
    [BsonId] 
    public ObjectId Id { get; set; } 
    public string SKU { get; set; } 
    public decimal Revision { get; set; } 
    public decimal Cost { get; set; } 
} 
+0

Questo sarebbe quasi certamente un approccio più efficiente con un grande set di risultati e/o oggetti di grandi dimensioni. Grazie. – ultra909

Problemi correlati