C# 2.0 Replacing documents with upsert

2,040 views
Skip to first unread message

Philip Bergström

unread,
May 22, 2015, 6:08:56 AM5/22/15
to mongod...@googlegroups.com
Hi,

I am trying to write a simple class using "Student" with the following format:

    public class MongoObject
    {
        public BsonObjectId id { get; set; }
    }

    public class Student : MongoObject
    {
        public string Name { get; set; }
        public int Year { get; set; }
    }

I want to perform an Update on the whole document (i.e replace), but also allow Upsert if the document doesn't exist:

       var filter = new BsonDocument("Name", "Philip");
       var upsert = new FindOneAndReplaceOptions<T> { IsUpsert = true };
       var result = await collection.FindOneAndReplaceAsync(filter, student, upsert); 

However, when I write the object, it doesn't receive any BSON id in the database:

 "_id" : {
    "_csharpnull" : true
  }

What am I doing wrong?
 

Craig Wilson

unread,
May 22, 2015, 8:07:50 AM5/22/15
to mongod...@googlegroups.com
Hi Phillip,

I think if you use ObjectId instead of BsonObjectId, it will work fine.

Craig

Philip Bergström

unread,
May 22, 2015, 12:04:07 PM5/22/15
to mongod...@googlegroups.com
I changed the MongoObject to:

    public class MongoObject 
    {
        [BsonId]
        public ObjectId id { get; set; }
    }

The id in the database isn't null anymore, but instead:

{
  "_id" : ObjectId("000000000000000000000000"),
  "name" : "Philip",
  "year" : 2000

Craig Wilson

unread,
May 22, 2015, 1:54:15 PM5/22/15
to mongod...@googlegroups.com
Replacements really only work if you know the identifier of the document you are replacing. I'd find it really weird, in this case, that you'd actually want to find a random document with the Name = "Philip" and replace it with something else entirely. If you really want to do that, then you might be able to add a [BsonIgnoreIfDefault] attribute on top of the identifier, so that we don't include the default id.  

Also, perhaps you could provide the business use case for this and I can help work through this problem in that regard and maybe come up with a different solution.

Craig

Philip Bergström

unread,
May 22, 2015, 4:27:33 PM5/22/15
to mongod...@googlegroups.com
That is what I suspected. I simply want a function performing an update if the document already exists and an insert if it doesn't. I guess I will solve it by adding an if-statement which will perform InsertOneAsync if the document doesn't exist.

However, I don't understand why the Replace method has an Upsert option, if it isn't designed to perform inserts?

--
You received this message because you are subscribed to the Google Groups "mongodb-user"
group.
 
For other MongoDB technical support options, see: http://www.mongodb.org/about/support/.
---
You received this message because you are subscribed to a topic in the Google Groups "mongodb-user" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mongodb-user/M1lNFLLWu7k/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mongodb-user...@googlegroups.com.
To post to this group, send email to mongod...@googlegroups.com.
Visit this group at http://groups.google.com/group/mongodb-user.
To view this discussion on the web visit https://groups.google.com/d/msgid/mongodb-user/cf1ae33b-7497-488f-b04a-a3fefb8f3f7e%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Craig Wilson

unread,
May 23, 2015, 9:02:13 AM5/23/15
to mongod...@googlegroups.com
It certainly is designed for that. However, for that to be useful most of the time, you need to be performing the match on a unique identifier. In most cases, the _id is used. So, replace this very particular document if it exists, otherwise upsert it.

Your scenario is very different. Replace any document where the Name is Philip if it exists, otherwise insert one. This is more difficult because the .NET driver will always serialize the _id field in the replacement document and, since it is a value type, will come out with it's default value if not set. You need to tell the driver not to serialize the _id if it is the default value. Plus, the _id field cannot be changed when doing the replacement, so it is actually pretty important that you don't serialize _id at all when not matching on _id.

My take on upserts is this. In most business cases, you know whether you are inserting or updating. I'd just do that. If you, as you stated above, have the ability to do a conditional statement, I'd wager a guess that you know if this is a new entity or not.

Hope that helps. Sorry it doesn't behave as expected. We are working through this particular scenario to try and make it more tractable.
Craig

Philip Bergström

unread,
May 23, 2015, 9:23:06 AM5/23/15
to mongod...@googlegroups.com
I see your point. However, from my experience, the Update function (with Upsert enabled) does exactly this without any problems. It doesn't change the _id if the document exists, and if it creates an _id if the document doesn't exist.

So basically, I would be able to solve this problem by using an UpdateModel instead of ReplaceModel, with an Update on every field. But an update on every field is the same as a replacement, which is why this issue confuses me.

Craig Wilson

unread,
May 23, 2015, 11:32:44 AM5/23/15
to mongod...@googlegroups.com
Right. It's not that it doesn't work. It's that with a .NET Poco where the Id has been mapped, we are serializing the _id. In an upsert, it means the server doesn't generate the id for you.  So, as I mentioned, one solution to this problem would be tell the driver to ignore the Id property when its value is the default. You can do this with the [BsonIgnoreIfDefault] attribute. I believe this will solve your problem.

Craig
Reply all
Reply to author
Forward
0 new messages