Search code examples
.net-coremongodb-.net-driver

FindOneAndUpdateAsync Intermittently Returning Null


I am using MongoDB.Driver for .NET Core 3.1 and running into an issue were records are not being saved properly. They are intermittently coming back as null when calling FindOneAndUpdateAsync. I have a script that calls my below code 100 times. Out of those 100, 1-5 fail in the last method, SetChildFavoritesAsync. The results came back as null. Any suggestions on what I am doing wrong?

Example Calls

var id = 1;
var childName = "test";
var collectionEntry = await FindByIdOrCreateAsync(id);

collectionEntry.Children = new List<MyCollection.ChildClass>{
  new MyCollection.ChildClass{
    Name = childName,
    Favorites = new List<MyCollection.ChildClass.Favorite>()
  }
};
await FindByIdAndUpdateChildrenAsync(collectionEntry.Id, collectionEntry.Children);

var favorites = new List<MyCollection.ChildClass.Favorite>{
  Name = "testFavorite"
};
var resultsOfSet = await SetChildFavoritesAsync(id, name, favorites)
//do stuff with resultsOfSet

Example Model

public class MyCollection
        {
            [MongoDB.Bson.Serialization.Attributes.BsonRepresentation(BsonType.ObjectId)]
            [MongoDB.Bson.Serialization.Attributes.BsonId]
            public string _Id { get; set; }
            [MongoDB.Bson.Serialization.Attributes.BsonRequired]
            public int Id { get; set; }
            public List<ChildClass> Children { get; set; }

            public class ChildClass
            {
                public string Name { get; set; }
                public List<Favorite> Favorites { get; set; }

                public class Favorite
                {
                    public string Name { get; set; }
                }
            }
        }

Example Methods

public async Task<MyCollection> FindByIdOrCreateAsync(int id)
        {
            var filter = Builders<MyCollection>.Filter.Eq(mc => mc.Id, id);
            var update = Builders<MyCollection>.Update
                            .Set(mc => mc.Id, id)
                            .SetOnInsert(mc => mc.Children, new List<MyCollection.ChildClass>());
            var options = new FindOneAndUpdateOptions<MyCollection> { ReturnDocument = ReturnDocument.After, IsUpsert = true };
            return await _database.GetCollection<MyCollection>("MyCollectionName").FindOneAndUpdateAsync(filter, update, options);
        }

public async Task<MyCollection> FindByIdAndUpdateChildrenAsync(int collectionId, List<MyCollection.ChildClass> children)
        {
            var filter = Builders<MyCollection>.Filter.Eq(mc => mc.Id, collectionId);
            var update = Builders<MyCollection>.Update.Set(mc => mc.Children, children);
            var options = new FindOneAndUpdateOptions<MyCollection> { ReturnDocument = ReturnDocument.After, IsUpsert = false };
            return await _database.GetCollection<MyCollection>("MyCollectionName").FindOneAndUpdateAsync(filter, update, options);
        }

public async Task<MyCollection> SetChildFavoritesAsync(int collectionId, string childName, List<MyCollection.ChildClass.Favorite> favorites)
        {
            var filter = Builders<MyCollection>.Filter.Eq(mc => mc.Id, collectionId);
            filter &= Builders<MyCollection>.Filter.Eq("children.name", childName);
            var update = Builders<MyCollection>.Update.Set("children.$.favorites", favorites);
            var options = new FindOneAndUpdateOptions<MyCollection> { ReturnDocument = ReturnDocument.After };
            var results = await _database.GetCollection<MyCollection>("MyCollectionName").FindOneAndUpdateAsync(filter, update, options);
            if (results == null)
            {
                _log.Error($"Child Favorites didn't save: collectionId:{collectionId}, childName:{childName}");
            }
            else
            {
                _log.Debug($"Child Favorites: collectionId:{collectionId}, childName:{childName}, favorites:{Newtonsoft.Json.JsonConvert.SerializeObject(results)}");
            }
            return results;
        }

Solution

  • Appears to be an issue with communication to the database. I added some retry logic, which solved the issue.