Search code examples
c#mongodbmongodb-.net-drivermongodb-csharp-2.0

When I use ReplaceOneAsync and IsUpsert = true mongodb add's a null Id. How do I stop this?


I am able to update a Document if the Document Exists using the Following

var filter = Builders<Neighborhood>.Filter.Eq(x => x.Id, neighborhood.Id);

var result = await collection.ReplaceOneAsync(filter,
             neighborhood,new UpdateOptions { IsUpsert = true });


[CollectionName("neighborhoods")]
[BsonIgnoreExtraElements(true)]
public class Neighborhood : IEntity<string>
{
 [BsonId(IdGenerator = typeof(GuidGenerator))]
 [BsonRepresentation(BsonType.ObjectId)]
 public string Id { get; set; }

 [BsonElement("name")]
 public string  Name    { get; set; }
}

How do Insert a Document if the Id = NULL and I want to return the updated result.

When Inserting a New Document NULL ID, A Record is created with a NULL Id, I added [BsonId(IdGenerator = typeof(GuidGenerator))] without any luck.

What am I doing wrong so a ObjectId can be generated for new records.


Solution

  • C# Driver thinks that Id is already filled. You need to add settings for Id field which allows driver to generate new Id.
    There are two approaches:

    1. Add attribute [BsonIgnoreIfDefault] in your Neighborhood model.
    2. Setup in code

      BsonClassMap.RegisterClassMap<Neighborhood>(x =>
      {
          x.AutoMap();
          x.GetMemberMap(m => m.Id).SetIgnoreIfDefault(true);
      });
      

    I prefer the second approach because you don't need to add reference to MongoDB.

    MongoDB API proposes two methods:
    1) ReplaceOneAsync returns ReplaceOneResult which has UpsertedId property

    var filter = Builders<Neighborhood>.Filter.Where(x => x.Name == "somthing");
    var replaceResult = await collection.ReplaceOneAsync(filter, entity, new UpdateOptions { IsUpsert = true });
    return replaceResult.UpsertedId;
    

    2) FindOneAndReplaceAsync allows you to select what you want - entity before changes or after. For our task we need after

    var filter = Builders<Neighborhood>.Filter.Where(x => x.Name == "somthing");
    var options = new FindOneAndReplaceOptions<Neighborhood, Neighborhood>
      {
         IsUpsert = true,
         ReturnDocument = ReturnDocument.After
      };
    var updatedEntity = await collection.FindOneAndReplaceAsync(filter, entity, options);