Reputation: 3823
I have a document e.g.
{
"_id": "ABC",
"name": "Bob",
"age": 42
}
I have 2 functions that run separate at separate times. Function A updates the name and function B updates the age. Both of these functions call an external rest service which brings back name or age depending on the service.
I have a class that maps to the mongo document
public class Person {
[BsonId]
public string Id { get; set; }
[BsonElement("name")]
public string Name { get; set; }
[BsonElement("age")]
public int Age { get; set; }
}
Function A will populate only the Id and the Name properties of this class whilst function B will populate the Id and Age properties.
How can I update the mongo document by just passing the Person object from function B so that it keeps the existing value of function A?
I know you can do Builders<Person>.Update.Set(x => x.Age, <new_age>)
but I want my code to be a bit more dynamic so that if I add another field called 'Phone' I can just call a generic method that will update only the fields that have a value and are not null.
Upvotes: 3
Views: 3251
Reputation: 473
Only add the elements you want to update to a new BsonDocument, in order to create your update definition.
private static void SetUpdatableElements(BsonDocument bdoc, BsonDocument updateDefintion, HashSet<string>() excluded = null, string parentName = "")
{
var excluded = excluded ?? new HashSet<string>()
parentName = !string.IsNullOrWhiteSpace(parentName) ? $"{parentName}." : "";
foreach (var item in bdoc)
{
if (item.Value.IsObjectId || // skip _id
item.Value.IsBsonNull || // skip properties with null values
excluded.Contains(item.Name)) // skip other properties that should not be updated
{
continue;
}
if (!item.Value.IsBsonDocument) // to avoid override nested objects)
{
updateDefintion = updateDefintion.Add($"{parentName}{item.Name}", item.Value);
continue;
}
// recursively set nested elements to avoid overriding the full object
SetUpdatableElements(item.Value.ToBsonDocument(), updateDefintion, item.Name);
}
}
The caller:
var person = new Person() { Id = "ABC", Name = "bob", Age:42, Phone="123 456 7890"}
var updateDefintion = new BsonDocument();
var bDoc = person.ToBsonDocument();
SetUpdatableElements(person.ToBsonDocument(), updateDefintion, new HashSet<string>(nameof(person.Name)))
collection.UpdateOneAsync<Person>(p => p.Id == person.Id, new BsonDocument("$set", updateDefintion));
At the end, your updateDefinition should be something like:
{ {
"$set": {
"age": 42
"phone": "123 456 7890"
}
} }
Upvotes: 2