2016-04-23 5 views
0

ich das folgende Klasse Design habe:mongoDB: V2 C# Treiber Wie Element in einer verschachtelten Sammlung aktualisieren

public class UserDayStore 
{ 
    public Guid Id { get; set; } 
    public string UserName { get; set; } 
    public string Password { get; set; } 
    public DateTime EndOfSubscription { get; set; } 
    public bool active { get; internal set; } 
    public DateTime LastModified; 

    public List<DbDayRecord> Days { get; set; } 
} 

mit

public class DbDayRecord 
{ 
    public Guid StoreId { get; set; } 
    public DateTime LastModified { get; set; } 
    public DateTime DateOfDay { get; set; } 

    public String Quote { get; set; } 

} 

ein UserDayStor funktioniert ohne Probleme Hinzufügen, auch einen verschachtelte Hinzufügen Artikel zu List<Days> ist in Ordnung. Wenn ich versuche, einen DayRecord zu aktualisieren, wird das Update ausgeführt, wie ich es in Robomongo sehen kann. Aber wenn ich versuche, ein Dokument nach diesem in der Users Sammlung suchen bekomme ich diese Ausnahme:

System.FormatException

Fehler beim Deserialisieren der Tage Eigenschaft der Klasse QuoteMyDayServer.ServerLogic.UserDayStore: Kann eine 'List' von BsonType 'Document' nicht deserialisieren.

bei MongoDB.Driver.Linq.MongoQueryProviderImpl 1.Execute(Expression expression) bei MongoDB.Driver.Linq.MongoQueryProviderImpl 1.Execute [TResult] (Expression expression) bei System.Linq.Queryable.First [TSource] (IQueryable 1 source) bei QuoteMyDayServer.ServerLogic.QuoteDatabase.<SetUserState>d__10.MoveNext() in C:\Entwicklung\Apps\QuoteMyDay\Server\QuoteMyDayServer\QuoteMyDayServer\ServerLogic\QuoteDatabase.cs:Zeile 147. --- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde --- bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) bei System.Runtime.CompilerServices.TaskAwaiter 1.GetResult() bei NancyTests.MongoTests.d__8 .MoveNext() in C: \ Entwicklung \ Apps \ QuoteMyDay \ Server \ QuoteMyDayServer \ NancyTests \ MongoTests.cs: Zeile 148. --- Ende der Stapelüberwachung vom vorherigen Ort, ein der die Ausnahme durchgeführt wurde System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (Task Aufgabe) bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Task Aufgabe) bei Xunit.Sdk.TestInvoker`1. <> c__DisplayClass46_1. < b__1> d.MoveNext() in C: \ BuildAgent \ work \ cb37e9acf085d108 \ src \ xunit.execution \ Sdk \ Frameworks \ Runners \ TestInvoker.cs: Zeile 227. --- Ende der Stapelüberwachung vom vorhergehenden Ort , eine DM ausnahme ausgelöst Wird --- bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (Aufgabe Aufgabe) bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Aufgabe Aufgabe) bei Xunit.Sdk.ExecutionTimer.d__4 sterben .MoveNext() in C: \ BuildAgent \ work \ cb37e9acf085d108 \ src \ xunit.execution \ Sdk \ Frameworks \ ExecutionTimer.cs: Zeile 48. --- Ende der Stapelüberwachung vom vorherg ehenden Ort, ein DM stirbt ausnahme ausgelöst Wird --- bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (Aufgabe Aufgabe) bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Aufgabe Aufgabe) bei Xunit.Sdk.ExceptionAggregator .d__9.MoveNext() in C: \ BuildAgent \ work \ cb37e9acf085d108 \ src \ xunit.core \ Sdk \ ExceptionAggregator.cs: Zeile 90.

System.FormatException

Kann nicht deserialisieren eine ‚Liste 'von BsonType' Dokument '.

bei MongoDB.Bson.Serialization.Serializers.EnumerableSerializerBase 2.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) bei MongoDB.Bson.Serialization.Serializers.SerializerBase 1.MongoDB.Bson.Serialization.IBsonSerializer.Deserialize (BsonDeserializationContext Zusammenhang BsonDeserializationArgs args) bei MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize (IBsonSerializer Serializer BsonDeserializationContext context) bei MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeMemberValue (BsonDeserializationContext Zusammenhang BsonMemberMap Membermap)

Dies ist die Methode, die die DayRecord, wenn man mit den Aktualisierungen Derselbe "DateOfDay" existiert, ansonsten wird ein neuer eingefügt.

public async Task<bool> UpdateDayRecord(Guid storeID, DbDayRecord dayRecord) 
    { 
     var stores = GetUserStores(); 

     var builder = Builders<UserDayStore>.Filter; 
     var filter = builder.Eq("Id", storeID); 
     var result = await stores.FindAsync(filter); 

     if (!await result.AnyAsync()) // Check is a Store with that Id exists 
     { 
      return false; 
     } 
     dayRecord.StoreId = storeID; 

     filter = builder.ElemMatch("Days", Builders<DbDayRecord>.Filter.Eq("DateOfDay", dayRecord.DateOfDay)); 
     result = await stores.FindAsync(filter); 


     if (await result.AnyAsync()) 
     { 

      var update = Builders<UserDayStore>.Update.Set("Days", dayRecord).CurrentDate("LastModified"); 

      var updateResult = await stores.UpdateOneAsync(filter, update); 

      return (updateResult.ModifiedCount == 1); 
     } 
     else 
     { 

      filter = Builders<UserDayStore>.Filter.Eq("Id", storeID); 
      var update = Builders<UserDayStore>.Update.AddToSet("Days", dayRecord).CurrentDate("LastModified"); 

      var updateResult = await stores.UpdateOneAsync(filter, update); 

      return (updateResult.ModifiedCount == 1); 
     } 
    } 

Nachdem die Methode aufgerufen wird und aktualisiert eine bestehendes DayRecord ich die Ausnahme oben bei dem Versuch, den Zugang der UserDayStor erhalten:

public async Task<Guid> GetStoreId (string username) 
    { 
     var stores = GetUserStores(); 

     var filter = Builders<UserDayStore>.Filter.Eq("UserName", username); 

     var result = await stores.FindAsync(filter); 

     return result.First().Id; 
    } 

er in dem Anruf FindAsync ausfällt.

Dies ist, was das JSON-Dokument wie

{ 
    "_id" : LUUID("e858f1cc-c81d-7244-b8a0-8beec3c8e10d"), 
    "LastModified" : ISODate("2016-04-23T10:43:17.293Z"), 
    "UserName" : "TestUser", 
    "Password" : "4242", 
    "EndOfSubscription" : ISODate("2016-06-22T22:00:00.000Z"), 
    "active" : true, 
    "Days" : { 
     "StoreId" : LUUID("e858f1cc-c81d-7244-b8a0-8beec3c8e10d"), 
     "LastModified" : ISODate("2016-04-24T00:00:00.000Z"), 
     "DateOfDay" : ISODate("2016-04-23T00:00:00.000Z"), 
     "Quote" : "Testquote1" 
    } 
} 

Antwort

0

Nach einigen Versuch und Irrtum ich glaube, ich kenne den richtigen Weg gefunden:

filter = builder.ElemMatch("Days", Builders<DbDayRecord>.Filter.Eq("DateOfDay", dayRecord.DateOfDay)); 
    result = await stores.FindAsync(filter); 

    if (await result.AnyAsync()) // Record already exists, update it 
    { 

     var update = Builders<UserDayStore>.Update.Set("Days.$", dayRecord).CurrentDate("LastModified"); 

     var updateResult = await stores.UpdateOneAsync(filter, update); 

     return (updateResult.ModifiedCount == 1); 
    } 
    else // Add new Record to array 
    { 

     filter = Builders<UserDayStore>.Filter.Eq("Id", storeID); 
     var update = Builders<UserDayStore>.Update.AddToSet("Days", dayRecord).CurrentDate("LastModified"); 

     var updateResult = await stores.UpdateOneAsync(filter, update); 

     return (updateResult.ModifiedCount == 1); 
    } 

Der wichtige Punkt liegt hier:

var update = Builders<UserDayStore>.Update.Set("Days.$", dayRecord).CurrentDate("LastModified"); 

die .$ macht Mongo Hinzufügen des Array-Element zu aktualisieren.

1

Gut nach dem Update sieht, sehe ich einige Fehler (wahrscheinlich Tippfehler), so gebe ich ihm einen Pass. Ihre GetStoreId Implementierung ist jedoch etwas riskant. Was ist, wenn es keinen passenden Store für einen bestimmten Benutzernamen gibt? Sie gehen davon aus, dass immer ein Dokument vorhanden ist, das falsch ist.

Ich habe geändert GetStoreId Implementierung ein wenig, so tauschen Sie es mit Ihrem und sehen, ob es funktioniert.

public async Task<Guid> GetStoreId (string username) 
{  
    var cursor = await collection.FindAsync(x => x.UserName == username); 

    var userDayStore = await cursor.FirstOrDefaultAsync(); 

    return userDayStore != null ? userDayStore.Id: Guid.Empty; 
} 
+0

Ok, ich Ihren Punkt sehen, aber dies alles nicht mit der Änderung Ausnahme. Ich bin neu für Mongo. Gibt es einen Vorteil der Verwendung von Lambda im FindAsync? Welche anderen Fehler siehst du? Der Code wird kompiliert? – Thomas

+0

Was meinst du damit ändert sich nichts mit Ausnahme? Hast du es versucht? – Saleem

+0

Sicher, nur versucht. Die Ausnahme ist in der FindAsync – Thomas

0

Dank der Hilfe von @Saleem habe ich das Problem gefunden.

vor dem Update der JSON Dokumente wie folgt aussehen:

{ 
    "_id" : LUUID("f7379cb0-bace-0442-9942-4452f8646522"), 
    "LastModified" : ISODate("2016-04-23T12:59:31.358Z"), 
    "UserName" : "TestUser", 
    "Password" : "4242", 
    "EndOfSubscription" : ISODate("2016-06-22T22:00:00.000Z"), 
    "active" : true, 
    "Days" : [ 
     { 
      "StoreId" : LUUID("f7379cb0-bace-0442-9942-4452f8646522"), 
      "LastModified" : ISODate("2016-04-24T00:00:00.000Z"), 
      "DateOfDay" : ISODate("2016-04-23T00:00:00.000Z"), 
      "Quote" : "Testquote1" 
     } 
    ] 
} 

So ist das Problem im Update liegt. Es hat die List zu einem DayRecord-Objekt geändert.

Sobald ich eine funktionierende Lösung habe, werde ich diese Frage aktualisieren

2

Ich glaube, Ihr Problem ist, dass Sie Ihre Update-Anweisung in einem Fall und AddToSet in einem anderen Fall Satz verwendet. AddToSet ist eine Array-basierte Operation und Set weist einen Wert direkt zu. Sie sollten AddToSet in beiden Fällen verwenden, um sicherzustellen, dass ein Array in MongoDB vorhanden ist.

Die FormatException ist, weil wir ein Array erwarten (weil der Typ List ist) und stattdessen erhalten wir ein Dokument.

+0

Mir war nicht bekannt, dass AdToSet in einem Update-Vorgang verwendet werden könnte. Bitte, bitte verbessern Sie die Dokumentation. – Thomas

+0

Warum müssen Sie das Dokument überhaupt deserialisieren, um eine Suche durchzuführen? Bedeutet das, dass für alle eingebetteten Dokumente das übergeordnete Dokument und alle eingebetteten Dokumente geladen werden? Dann wäre es vielleicht besser, die Listen in eine separate Sammlung zu stellen. – Thomas

+0

1. In Ihrem obigen Code verwenden Sie AddToSet bereits in der else-Anweisung. Scheint so, als hättest du es schon gefunden. 2. Wir deserialisieren nur Dokumente für einen gefundenen Fund. Der Server sendet alle gefundenen Dokumente zurück und wir deserialisieren sie alle. 3. Schema-Design ist ein komplexes Thema und ob Dinge in ihre eigene Sammlung oder eingebettet werden, hängt wirklich von Ihrem Anwendungsfall ab. –

Verwandte Themen