why does my records got zero object id in mongodb upsert scenario?

Viewed 487

I write an extension method to implement the upsert(update if exists else insert) scenario but every time I use it, my records save with "0" object id in my mongodb document. here is my code:

public static async Task<ReplaceOneResult> UpsertAsync<T>
        (this IMongoCollection<T> collection, T entity) where T : IEntity
    {
        return await collection.ReplaceOneAsync(i => i.Id == entity.Id,
            entity,
            new ReplaceOptions { IsUpsert = true });
    }

and my model:

public class User : IEntity
{
    [BsonId]
    public ObjectId Id { get; set; }
    public int TelegramUserId { get; set; }
    public bool IsBot { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Username { get; set; }
    public string ActivityPath { get; set; }
    public string Number { get; set; }
    public string Location { get; set; }
    public ObjectId [] Playlists { get; set; }
}

and my saved record looks like this:

{
"_id": {
    "$oid": "000000000000000000000000"
},
"TelegramUserId": 515151,
"IsBot": false,
"FirstName": "Test user",
"LastName": null,
"Username": "Test",
"ActivityPath": null,
"Number": null,
"Location": null,
"Playlists": null}

the problem is "000000000000000000000000" value for id, why id don't init correctly, I expect a valid guid value for that, not just zeros.

thanks for your help.

1 Answers

A MongoId with all zeroes is valid, so the first insert with an all zeroes or "empty" MongoId is not matching on your condition

i => i.Id == entity.Id

causing an insert, but subsequent conditions with an all-zero ID are matching and so they do an update.

Here's what I usually do:

void T Update<T>(T entity) where T : IEntity
{
    // Id is an ObjectId (which is a .NET struct)
    if (entity.Id == ObjectId.Empty)
        entity.Id = ObjectId.GenerateNewId()
    ...
}

This says, if the ID is the all-zeroes ID (i.e. Empty), generate one (which will be unique; your insert) or keep the existing (your update).

It's a bit more than you asked for on this question, but there's another way to deal with this if you are mapping ObjectId to a string, which occurs in some systems. If that's the case you would do

    // if the Id field is a string that can sometimes be the string version of ObjectId
    entity.Id ??= ObjectId.GenerateNewId().ToString()

when your entity looks something like this

public class User : IEntity
{
    [BsonId]
    public string Id { get; set; }
    ...
Related