Search code examples
c#.netmongodbmongodb-.net-driver

How to update but not replace a document using updateOne() of the MongoDB-driver for .NET


Situation:

I have an existing collection of documents in a MongoDB in which I want to update the fields of a specific document with the fields of an incoming document. The incoming document has the same class as the documents in the collection, but not all of its fields are set, i.e. some fields are null. Now, I don't want to replace the entire document in the collection, neither do I want to replace all fields, but I want to replace only those fields which are not null and keep the rest as is.

This SO answer describes how to replace an entire document. I got this working with this code:

public void Update(string id, MyDoc docIn) => _docs.ReplaceOne(doc =>  scan.Id.Equals(ObjectId.Parse(id)), docIn);

In order to update individual fields in the document, the docs suggest creating an "Update Operator Expressions Document".

My question:

Does this mean that I have to write my own function turning my incoming document into such an "Update Operator Expressions Document" or is there a more convenient way?


Solution

  • As you already said, using an Update Operator is the way to go.
    The idea is to use a filter and use a Reflection of an Object that goes through the Properties.

    You can modify the Update and Filter Function as you want.

    using System.Reflection;
    
    public void Update(string id, MyDoc docIn) 
       => _docs.UpdateOne(doc =>  doc.Id.Equals(ObjectId.Parse(id)), UpdateDocument(docIn);
    
    public UpdateDefinition<myDoc> UpdateDocument(myDoc docIn) 
    {
        var builder = Builders<MyDoc>.Update.Set(u => u.Id, docIn.Id);
        foreach(PropertyInfo prop in docIn.GetType().GetProperties())
        {
             var value = docIn.GetType().GetProperty(prop.Name).GetValue(docIn,null);
             if ((prop.Name != "Id") & (value != null))
             {
                  builder = builder.Set(prop.Name, value);
             }
        }
        return builder;
    }