Question

Consider the following class

public class Something 
{
    public ObjectId Id;
    public DateTime DbUpdatedAt;
    public string AnotherProperty;
    public int SomeIntProp;
}

I would normally do a partial update with the following code

var obj = ... // an instance of Something
var update = new UpdateBuilder<Something>();
update.Set(x => x.DbUpdatedAt, DateTime.UtcNow);
...
/// later on, 
// database is an instance of MongoDatabase
database.GetCollection("CollectionName")
        .Update(Query<Something>.Eq(x => x.Id, something.Id), update);

The problem is, I am not aware of any way to check whether update already has been configured to set a value for DbUpdatedAt. If I blindly try to set a new value for DbUpdatedAt, I get an error.

...
/// later on, 
update.Set(x => x.DbUpdatedAt, DateTime.UtcNow.AddHours(1)); // this throws a Duplicate element name error 
// database is an instance of MongoDatabase
database.GetCollection("CollectionName")
        .Update(Query<Something>.Eq(x => x.Id, something.Id), update);

I understand WHY the error happens. I need a way to,

  1. Detect there is a duplicate key scenario,
  2. Replace the old key,value pair with the new key,value pair.
Was it helpful?

Solution

The UpdateBuilder as currently implemented considers trying to set the same field to two different values an error (though I wish the exception thrown was more clear...).

It's not unreasonable to expect that calling Set for the same field twice would simply override the first one with the second one, but that's not how it currently works. If you feel it should work that way please file a JIRA ticket suggesting the change.

As a workaround, you could define an extension method on UpdateBuilder that behaves the way you want it to. It would probably look like this:

public static UpdateBuilder<TDocument> SetWithOverride<TDocument, TField>(this UpdateBuilder<TDocument> update, Expression<Func<TDocument, TField>> memberExpression, TField value)
{
    var set = Update<TDocument>.Set(memberExpression, value).ToBsonDocument();
    var combined = update.ToBsonDocument();
    if (combined.Contains("$set"))
    {
        var element = set[0].AsBsonDocument.GetElement(0);
        combined["$set"][element.Name] = element.Value;
    }
    else
    {
        combined.Merge(set);
    }
    return Update<TDocument>.Combine(combined.Elements.Select(e => new UpdateDocument(e)));
}

And here's some sample code showing it being used:

var update = Update<C>.Set(c => c.X, 1).Set(c => c.Y, 2);
update = update.SetWithOverride(c => c.Y, 3);

OTHER TIPS

From the manuals:

Use the $set operator to replace the value of a field to the specified value. If the field does not exist, the $set operator will add the field with the specified value.

From your description, you shouldn't be getting any duplicate key errors, unless you've made DbUpdatedAt part of some unique index or something.

I think a more common mistake would be to insert a document rather than to update one (which is easy to do using the C# driver) but that won't be happening if you're doing an update using Mongo's query builders, as you appear to be doing.

All in all, the error you're getting doesn't make sense. Are you sure there's not something else going on that is causing this error?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top