2 / 3
May 2024

I’m trying to upsert a document into Mongo using the C# Driver with the following (simplified) method:

public MyDocumentType UpsertOne(MyDocumentType doc) { var options = new FindOneAndReplaceOptions<MyDocumentType> { IsUpsert = true, ReturnDocument = ReturnDocument.After, }; var filter = Builders<MyDocumentType>.Filter.Eq(d => d.Id, doc.Id); var upsertedDoc = _collection.FindOneAndReplace(filter, doc, options); return doc; }

Neither the upsertedDoc nor doc objects have a nonzero objectID.

However, When I insert the doc with InsertOne() Mongo generates a docID as expected:

public MyDocumentType InsertOne(MyDocumentType doc) { _collection.InsertOne(doc); return doc; }

The ObjectId field in my document class is nonnullable, and initializes to zeros, but on InsertOne it gets proper documentID but when using FindOneAndReplace with the IsUpsert=true option it simply inserts a doc with a zero’d Id.

Here is how I’m calling the various methods, and the output:

var db = new MongoService(); db.Connect(); var documentWithZeroedId = db.UpsertOne(document); Console.WriteLine($"Upsert Document ID: {documentWithZeroedId.Id}"); var documentWithValidId = db.InsertOne(document); Console.WriteLine($"Insert Document ID: {documentWithValidId.Id}"); // Output: Connected to Mongo! Upsert Document ID: 000000000000000000000000 Insert Document ID: 6620b59ca4421c3c294c0b09

What is the proper means to upsert a doc and when inserting, get a valid non-zeroed doc ID?

The complete console app demonstrating this issue is:

using MongoDB.Driver; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using System.Security.Cryptography.X509Certificates; using System.Xml.Xsl; public class DictionaryValue { public int Id; public string Value; } [AttributeUsage(AttributeTargets.Class, Inherited = false)] public class BsonCollectionAttribute : Attribute { public string CollectionName { get; } public BsonCollectionAttribute(string collectionName) { CollectionName = collectionName; } } [BsonCollection("Documents")] public class MyDocumentType { [BsonId] [BsonRepresentation(MongoDB.Bson.BsonType.ObjectId)] public ObjectId Id; public DateTime CreatedAt => Id.CreationTime; public string TopLevelField; public Dictionary<string, DictionaryValue> Values; } class MongoService { private MongoClient _client; private IMongoDatabase _db; private IMongoCollection<MyDocumentType> _collection; public void Connect() { try { var settings = MongoClientSettings.FromConnectionString("mongodb://localhost:27018"); _client = new MongoClient(settings); _db = _client.GetDatabase("dictionaryTest"); Console.WriteLine("Connected to Mongo!"); _collection = _db.GetCollection<MyDocumentType>("Documents"); } catch (Exception ex) { Console.WriteLine("Error on connect to Mango database: {error}", ex); throw; } } public MyDocumentType UpsertOne(MyDocumentType doc) { var options = new FindOneAndReplaceOptions<MyDocumentType> { IsUpsert = true, ReturnDocument = ReturnDocument.After, }; var filter = Builders<MyDocumentType>.Filter.Eq(d => d.Id, doc.Id); var upsertedDoc = _collection.FindOneAndReplace(filter, doc, options); return doc; } public MyDocumentType UpsertOneSlow(MyDocumentType doc) { var existing = _collection.Find(d => d.Id == doc.Id).FirstOrDefault(); if (existing != null) { _collection.ReplaceOne(d => d.Id == doc.Id, doc); return doc; } else { _collection.InsertOne(doc); return doc; } } public MyDocumentType InsertOne(MyDocumentType doc) { _collection.InsertOne(doc); return doc; } } class Program { static int Main(String[] args) { var document = new MyDocumentType(); document.TopLevelField = "Dictionary of Integers"; document.Values = new Dictionary<string, DictionaryValue>() { { "1", new DictionaryValue {Id = 1, Value = "1"} }, { "2", new DictionaryValue {Id = 2, Value = "1"} }, { "3", new DictionaryValue {Id = 3, Value = "2"} }, }; var db = new MongoService(); db.Connect(); var documentWithZeroedId = db.UpsertOne(document); Console.WriteLine($"Upsert Document ID: {documentWithZeroedId.Id}"); var documentWithValidId = db.InsertOne(document); Console.WriteLine($"Insert Document ID: {documentWithValidId.Id}"); return 0; } }
1 month later

Hi Tim,

Thanks for posting this. The behavior outlined does not seem to be expected and you should be getting a valid object ID back. I’ve created a ticket for our team to validate this which you can follow for updates.

Thanks,

Rishit.