Error on update - BsonSerializationException: Element name '_id' is not valid'.

4,882 views
Skip to first unread message

Grant Megrabyan

unread,
Feb 2, 2015, 7:38:50 PM2/2/15
to mongodb...@googlegroups.com
I've got strange error when I try to update document: BsonSerializationException: Element name '_id' is not valid'.
Here is my code:

public async Task UpdateAsync(Entity entity, CancellationToken cancellationToken = new CancellationToken())
{
    cancellationToken.ThrowIfCancellationRequested();
    if (entity == null)
    {
        throw new ArgumentNullException("entity");
    }

    await _context.Entities.UpdateOneAsync(a => a.Id == entity.Id, entity, new UpdateOptions {IsUpsert = true}, cancellationToken: cancellationToken);
}

_context.Entities - is IMongoCollection<Entity>

I went through source code and found that validation is happening in BsonWriter.WriteName():

...
if (!_elementNameValidator.IsValidElementName(name))
{
    var message = string.Format("Element name '{0}' is not valid'.", name);
    throw new BsonSerializationException(message);
}
...

I guess this is the place where exception is thrown.

Could you please help me figure out what am I doing wrong?

Craig Wilson

unread,
Feb 2, 2015, 7:52:33 PM2/2/15
to mongodb...@googlegroups.com
It looks to me like you are trying to replace your entity, not update it. I believe what is happening is that we are validating that you haven't specified an _id in the update document. This is because updates don't let you change _id. _id is immutable. The problem here is that the id is the same. So, this is certainly an interesting problem. I'll bring this up with my colleagues. However, for now, I think if you just use ReplaceOneAsync, all will be well and it's what you want anyways.

Grant Megrabyan

unread,
Feb 3, 2015, 5:29:33 AM2/3/15
to mongodb...@googlegroups.com
Thank you, it is what i need!

Steve Vaneeckhout

unread,
Feb 25, 2015, 3:32:10 PM2/25/15
to mongodb...@googlegroups.com
How do we do an upsert with UpdateOneAsync() ?

Op dinsdag 3 februari 2015 01:52:33 UTC+1 schreef Craig Wilson:

Craig Wilson

unread,
Feb 25, 2015, 4:31:31 PM2/25/15
to mongodb...@googlegroups.com
You pass UpdateOptions with IsUpsert set to true...

col.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true });

The same is true for UpdateManyAsync and ReplaceOneAsync.

Steve Vaneeckhout

unread,
Feb 26, 2015, 4:44:34 PM2/26/15
to mongodb...@googlegroups.com
That's what I'm doing but I'm getting the same exception as Grant. I'm passing a simple class as the second parameter.

public class WikiPage

{

public ObjectId Id { get; set; }

public string TitleSlug { get; set; }

public string Title { get; set; }

public string Intro { get; set; }

public string Contents { get; set; }

public DateTime Created { get; set; }

public DateTime Edited { get; set; }

}

It doesn't matter if the Id property has a value or not (because it's a new or an existing object) I always get the same exception.


Op woensdag 25 februari 2015 22:31:31 UTC+1 schreef Craig Wilson:

Craig Wilson

unread,
Feb 26, 2015, 5:47:42 PM2/26/15
to mongodb...@googlegroups.com
Are you sure you want to use Update? If you are using an entire entity like this, you probably want ReplaceOneAsync... with the IsUpsert boolean set.  This was the solution already proposed previously on this thread.

Steve Vaneeckhout

unread,
Feb 27, 2015, 2:09:06 PM2/27/15
to mongodb...@googlegroups.com
Right, I tried out ReplaceOneAsync which doesn't throw the exception but when a new object is added to the collection the Id property stays {00000...}.

(I just started out with MongoDB and the beta drivers, so all this stuff is still very new to me)

Op donderdag 26 februari 2015 23:47:42 UTC+1 schreef Craig Wilson:

Craig Wilson

unread,
Feb 27, 2015, 4:12:32 PM2/27/15
to mongodb...@googlegroups.com
There are certainly some situations that might currently be unaccounted for. Good news is it is in beta form, so we can still fix them. In this case, could you provide a more complete situation? What filter are you using? Do you know the _id of the document you are trying to replace?

Steve Vaneeckhout

unread,
Mar 1, 2015, 3:40:53 PM3/1/15
to mongodb...@googlegroups.com
I created a Github repository that inserts a document into MongoDB using InsertOneAsync(), ReplaceOneAsync() and UpdateOneAsync(). For inserting a new document I expect (since there is not much 2.0 documentation) these 3 functions to behave the same: don't throw exceptions, create a valid Id and populate the Id property of my strongly typed object. So far only InsertOneAsync() behaves correctly.

https://github.com/SteveVaneeckhout/MongoInsertTest/blob/master/InsertTest/Program.cs

Op vrijdag 27 februari 2015 22:12:32 UTC+1 schreef Craig Wilson:

Craig Wilson

unread,
Mar 1, 2015, 5:17:05 PM3/1/15
to mongodb...@googlegroups.com
For insert, we generate the identifiers client-side. If we did that for replaceOne or updateOne, then you'd be attempting to overwrite a document's identity, which is not allowed. Hence, the server generates the identities for replaceOne and UpdateOne.

However, replaceOne is certainly a unique issue here in that we shouldn't even send an _id to the server for the replacement entity if that _id is empty (has a null value or the default empty value for a Guid or ObjectId). We are discussing this currently. However, insertOne, updateOne, and replaceOne are not intended to behave the same and will not behave the same way. Hence, the 3 different methods with 3 different purposes. 

Steve Vaneeckhout

unread,
Mar 2, 2015, 3:00:36 PM3/2/15
to mongodb...@googlegroups.com
I think I understand the difference between updateOne and replaceOne when updating an existing document. But why would there be a difference in behavior when inserting a new document? If the filter (p => p.Id == person.Id) returns null you know you have to insert a new document. There is no chance of overwriting an existing document because there isn't one.

Also, I looked at the result of ReplaceOne: UpsertedId. It has the same value {00000} and in MongoDB the Id is also {00000}.


Op zondag 1 maart 2015 23:17:05 UTC+1 schreef Craig Wilson:

Craig Wilson

unread,
Mar 2, 2015, 3:26:34 PM3/2/15
to mongodb...@googlegroups.com
Right... I think we need to correct something about how ReplaceOne works. We aren't going to generate an _id for it, but we can "not" send one up.

You said: "If the filter (p => p.Id == person.Id) returns null you know you have to insert a new document".
  We aren't running that filter client-side and then inserting a document based on the result. This all happens on the server. In a used system, doing it this way would be a recipe for data problems and duplicate key issues. Hence, if you know you should insert a document (and presumably) you do, then use InsertOne. Otherwise, you can use ReplaceOne with Upsert, but the semantics will not be the same and any _id that gets generated will be generated by the server, not the client.
Reply all
Reply to author
Forward
0 new messages