Yurii Hohan
Yurii Hohan

Reputation: 4171

Upserting in Mongo DB and the Id problem

I have a problem while upserting to mongo db using the official C# driver.

public abstract class AggregateRoot
{
    /// <summary>
    /// All mongoDb documents must have an id, we specify it here
    /// </summary>
    protected AggregateRoot()
    {
        Id = ObjectId.GenerateNewId();
    }

    [BsonId]
    public ObjectId Id { get; set; }
}

My entities already have the id-s but I had to create the mongo specific Id for it to work, as all the documents in a collection should have one. Now then I receive a new entity in my system a new Mongo Id is generated and I get the mongo cannot change _id of a document old exception. Is there some work-around?

Let me describe the design a bit. All the entities which would be stored as documents were inheriting from AggregateRoot which had the id generation in it. Every sub-document had its id generated automatically and I had no problem with this. The id in AggregateRoot was introduced to correct the problem when retrieving data from MongoCollection to List and the generation was introduced so the id-s are different. Now we can move that id generation to save methods because the new entity for update had a new id generation. But it would mean that every dev on the team must not forget generating id-s in repository which is risky. It would be nicer just to ignore the id than mapping from mongo if it is possible and not to have AggregateRoot class at all

Upvotes: 20

Views: 24054

Answers (2)

renadeen
renadeen

Reputation: 1844

I've encountered similar problem. I wanted to upsert documents using official C# driver. I had a class like this:

public class MyClass
{
    public ObjectId Id { get; set; }
    public int Field1 { get; set; }
    public string Field2 { get; set; }
}

In console I would write: db.collection.update({Field1: 3},{Field1: 3, Field2: "value"}) and it would work. In C# I wrote:

collection.Update(Query.EQ("Field1", 3),
                Update.Replace(new MyClass { Field1 = 3, Field2 = "value" }),
                UpdateFlags.Upsert);

and it didn't work! Because driver includes empty id in update statement and when I upsert second document with different value of Field1 exception E11000 duplicate key error index is thrown (in this case Mongo tries to insert a document with _id that already exists in db).

When I generated _id by myself (like topic starter) I've encountered the same exception (mongo cannot change _id of a document) on upserting objects with existing value of Field1.

Solution is to mark Id property by attribute [BsonIgnoreIfDefault] (and not initialize it). In this case driver omits _id field in update statement and MongoDb generates Id if it necessary.

Upvotes: 87

Chris Fulstow
Chris Fulstow

Reputation: 41902

Looks like you might be explicitly setting the Id value for both inserts and updates. That's fine for inserts, all new objects need an _id value, however for updates you're not allowed to change the value of _id on an existing document after it's created.

Try not setting the Id value at all. If you don't specify a value before inserting, the driver uses built-in IdGenerator classes to generate a new _id value, so if it's an ObjectId type it'll use the ObjectIdGenerator. Then both your inserts and updates work fine.

Upvotes: 8

Related Questions