Reputation: 9257
I have the following Class design:
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; }
}
with
public class DbDayRecord
{
public Guid StoreId { get; set; }
public DateTime LastModified { get; set; }
public DateTime DateOfDay { get; set; }
public String Quote { get; set; }
}
Adding a UserDayStore works without a problem, also Adding a nested Item to List<Days>
is ok. When I try to update a DayRecord, the Update is done as I can see in Robomongo. But when I try to search for a document after this in the UserStore collection I get this Exception:
System.FormatException
An error occurred while deserializing the Days property of class QuoteMyDayServer.ServerLogic.UserDayStore: Cannot deserialize a 'List' from BsonType 'Document'.
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](IQueryable1 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 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 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, an dem die Ausnahme ausgelöst wurde --- bei System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) bei Xunit.Sdk.ExecutionTimer.d__4.MoveNext() in C:\BuildAgent\work\cb37e9acf085d108\src\xunit.execution\Sdk\Frameworks\ExecutionTimer.cs:Zeile 48. --- 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 Xunit.Sdk.ExceptionAggregator.d__9.MoveNext() in C:\BuildAgent\work\cb37e9acf085d108\src\xunit.core\Sdk\ExceptionAggregator.cs:Zeile 90.System.FormatException
Cannot deserialize a 'List' from BsonType 'Document'.
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 context, BsonDeserializationArgs args) bei MongoDB.Bson.Serialization.IBsonSerializerExtensions.Deserialize(IBsonSerializer serializer, BsonDeserializationContext context) bei MongoDB.Bson.Serialization.BsonClassMapSerializer`1.DeserializeMemberValue(BsonDeserializationContext context, BsonMemberMap memberMap)
This is the method that Updates the DayRecord if one with the same "DateOfDay" exists, otherwise it will insert a new one.
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);
}
}
After the method is called and Updates an existing DayRecord I get the Exception above when trying access the UserDayStore:
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;
}
It fails in the FindAsync call.
This is what the JSON Document looks like after the update
{
"_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"
}
}
Upvotes: 1
Views: 2940
Reputation:
Instead of
var update = Builders<UserDayStore>.Update.Set("Days.$", dayRecord).CurrentDate("LastModified");
you can also do it like this:
var update = Builders<UserDayStore>.Update.Set(x => x.Days[-1], dayRecord).CurrentDate("LastModified");
Upvotes: 0
Reputation: 9257
After some try and error I think I know found the right way:
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);
}
The important point lies here:
var update = Builders<UserDayStore>.Update.Set("Days.$", dayRecord).CurrentDate("LastModified");
Adding the .$
makes mongo updating the array element.
Upvotes: 1
Reputation: 12624
I believe your problem is that your Update statement uses Set in one case and AddToSet in another case. AddToSet is an array based operation, and Set assigns a value directly. You should use AddToSet in both circumstances in order to ensure an array exists in MongoDB.
The FormatException is because we are expecting an array (because the type is List) and instead we get a document.
Upvotes: 2
Reputation: 9257
Thanks to the help of @Saleem I found the problem.
Before the Update the JSON Documents looks like this:
{
"_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 the problem lies in the Update. It changed the List to a DayRecord object.
As soon as I have a working solution I will update this question
Upvotes: 0
Reputation: 8978
Well, I see couple of errors (most probably typo) so I give it a pass. However, your GetStoreId
implementation is bit risky. What if there is no matching Store for a given username. You are assuming there will always be a document present which is wrong.
I have changed GetStoreId
implementation little bit so swap it with yours and see if it work.
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;
}
Upvotes: 1