2014-01-20 2 views
2

рассмотрим следующий классКак избежать ошибки «Дублировать Имя элемента» при построении UpdateBuilder

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

я обычно делаю частичное обновление с помощью следующего кода

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); 

Проблема в том, я я не знаю ни одного способа проверить, было ли уже установлено update для установки значения для DbUpdatedAt. Если я вслепую попытаюсь установить новое значение для DbUpdatedAt, я получаю сообщение об ошибке.

... 
/// 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); 

Я понимаю, ПОЧЕМУ произошла ошибка. Мне нужен способ,

  1. Detect есть дубликат ключа сценария,
  2. Заменить старый ключ, пару значений с новым ключом, значение пары.

ответ

2

UpdateBuilder, реализованный в настоящее время, предполагает попытку установить одно и то же поле на два разных значения ошибки (хотя я хочу, чтобы исключение было сброшено более ясно ...).

Неразумно ожидать, что вызов Set для того же поля дважды будет просто переопределять первый со вторым, но это не так, как в настоящее время работает. Если вы чувствуете, что это должно сработать, пожалуйста, напишите билет JIRA, предлагающий изменение.

В качестве обходного пути вы можете определить метод расширения для UpdateBuilder, который ведет себя так, как вы хотите. Это, вероятно, выглядеть следующим образом:

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))); 
} 

А вот пример кода показывает, что используется:

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

Спасибо. Это сработало хорошо.Для этого во всех случаях мне всегда придется использовать SetWithOverride (поскольку я не уверен, что другая функция уже вызвала set или нет). Имея это в виду, имеет ли эта функция какие-либо проблемы с производительностью? Код преобразует два оператора обновления в документы bson, а затем обратно в инструкцию обновления. Все ли десериализация/сериализация влияет на многие вызовы? –

+0

Кроме того, я создал проблему JIRA (https://jira.mongodb.org/browse/CSHARP-902). Надеюсь, он скоро будет реализован :) –

0

От the manuals:

Используйте $ оператор множества заменить значение поля до указанного значения. Если поле не существует, оператор $ set добавит поле с указанным значением.

Из вашего описания, вы не должны получать повторяющиеся ключевые ошибки, если вы не сделали DbUpdatedAt часть некоторого unique index или что-то.

Я думаю, что более распространенной ошибкой было бы вставить документ, а не обновлять его (что легко сделать с использованием драйвера C#), но это не произойдет, если вы делаете обновление с использованием построителей запросов Mongo , как вы, кажется, делаете.

В целом, ошибка, которую вы получаете, не имеет смысла. Вы уверены, что что-то еще не происходит, что вызывает эту ошибку?

+0

Спасибо за ответ. Однако проблема, с которой я сталкиваюсь, отличается. Как написано код, это приводит к следующему документу: '{$ set: {'DbUpdated': <некоторое значение даты>, 'DbUpdated': <другое значение даты>}}' ... Документ имеет тот же ключ 'DbUpdated' дважды, что неверно. @RobertStam предлагает обходное решение, которое проверяет, содержит ли документ уже ключ и обрабатывает его соответствующим образом. –

Смежные вопросы