When attempting to perform an upsert operation in Mongo, I'd like to have it generate a GUID for the ID instead of an Object ID. In this case, I'm checking to make sure an object with specific properties doesn't already exist and actually throwing an exception if the update occurs.Here's a stub of the class definition:public class Event { [BsonId(IdGenerator = typeof(GuidGenerator) )] [BsonRepresentation(BsonType.String)] [BsonIgnoreIfDefault] public Guid Id { get; set; } // ... more properties and junk}And here is how we are performing the upsert operation:// query to see if there are any pending operationsvar keyMatchQuery = Query<Event>.In(r => r.Key, keyList);var statusMatchQuery = Query<Event>.EQ(r => r.Status, "pending");var query = Query.And(keyMatchQuery , statusMatchQuery );var updateQuery = new UpdateBuilder();var bson = request.ToBsonDocument();foreach (var item in bson){ updateQuery.SetOnInsert(item.Name, item.Value);}var fields = Fields<Request>.Include(req => req.Id);var args = new FindAndModifyArgs(){ Fields = fields, Query = query, Update = updateQuery, Upsert = true, VersionReturned = FindAndModifyDocumentVersion.Modified};// Perform the upsertvar result = Collection.FindAndModify(args);Doing it this way will generate the ID as an ObjectID rather than a GUID.I can definitely get the behavior I want as a two step operation by performing a .FindOne first, and if it fails, doing a direct insert:var existingItem = Collection.FindOneAs<Event>(query);if (existingItem != null){ throw new PendingException(string.Format("Event already pending: id={0}", existingItem.Id));}var result = Collection.Insert(mongoRequest);In this case, it correctly sets the GUID for the new item, but the operation is non-atomic. I was searching for a way to set the default ID generation mechanism at the driver level, and thought this would do it:BsonSerializer.RegisterIdGenerator(typeof(Guid), GuidGenerator.Instance);...but to no avail, and I assume that's because for the upsert, the ID field can't be included so there is no serialization happening and Mongo is doing all of the work. I also looked into implementing a convention, but that didn't make sense since there are separate generation mechanisms to handle that. Is there a different approach I should be looking at for this and/or am I just missing something? I do realize that GUIDs are not always ideal in Mongo, but we are exploring using them due to compatibility with another system. 解决方案 What's happening is that only the server knows whether the FindAndModify is going to end up being an upsert or not, and as currently written it is the server that is automatically generating the _id value, and the server can only assume that the _id value should be an ObjectId (the server knows nothing about your class declarations).Here's a simplified example using the shell showing your scenario (minus all the C# code...):> db.test.drop()> db.test.find()> var query = { x : 1 }> var update = { $setOnInsert : { y : 2 } }> db.test.findAndModify({ query: query, update : update, new : true, upsert : true }){ "_id" : ObjectId("5346c3e8a8f26cfae50837d6"), "x" : 1, "y" : 2 }> db.test.find(){ "_id" : ObjectId("5346c3e8a8f26cfae50837d6"), "x" : 1, "y" : 2 }>We know this was an upsert because we ran it on an empty collection. Note that the server used the query as an initial template for the new document (that's where the "x" came from), applied the update specification (that's where the "y" came from), and because the document had no "_id" it generated a new ObjectId for it.The trick is to generate the _id client side in case it turns out to be needed, but to put it in the update specification in such a way that it only applies if it's a new document. Here's the previous example using $setOnInsert for the _id:> db.test.drop()> db.test.find()> var query = { x : 1 }> var update = { $setOnInsert : { _id : "E3650127-9B23-4209-9053-1CD989AE62B9", y : 2 } }> db.test.findAndModify({ query: query, update : update, new : true, upsert : true }){ "_id" : "E3650127-9B23-4209-9053-1CD989AE62B9", "x" : 1, "y" : 2 }> db.test.find(){ "_id" : "E3650127-9B23-4209-9053-1CD989AE62B9", "x" : 1, "y" : 2 }>Now we see that the server used the _id we supplied instead of generating an ObjectId.In terms of your C# code, simply add the following to your updateQuery:updateQuery.SetOnInsert("_id", Guid.NewGuid().ToString());You should consider renaming your updateQuery variable to updateSpecification (or just update) because technically it's not a query.There's a catch though... this technique is only going to work against the current 2.6 version of the server. See: https://jira.mongodb.org/browse/SERVER-9958 这篇关于MondDB C#Upsert与Guid的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
11-01 19:26