Условное обновление C#/MongoDB.Driver 2.29 – как выполнить обновление в коллекцию, но ничего не делать, если сохраненныйC#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Условное обновление C#/MongoDB.Driver 2.29 – как выполнить обновление в коллекцию, но ничего не делать, если сохраненный

Сообщение Anonymous »

Я хотел бы добавить (вставить, если соответствующий идентификатор не существует, обновить, если соответствующий идентификатор существует) документ в существующую коллекцию, но с оговоркой, что существует целочисленное свойство версии и если что-то уже сохранилось в коллекции имеет более высокую версию, чем та, которую мы пытаемся обновить, тогда обновление ничего не делает.
При таком определении тестового документа:
public record TestDocument(string Id, int Version, string SomeDataString);

Мы должны ожидать, что:
  • добавление документа с Id="Foo" и Version=[anything] работает если в коллекции еще нет Id="Foo", вставка нового документа
  • добавление документа с Id="Foo" и Version=2 работает, если есть Id="Foo" документ с версией 1 (все, что меньше 2), замена существующего документа
  • добавление документа с Id="foo" и Version=2 ничего не дает, если есть Id="Foo" документ с версией 2 или выше
Поскольку идентификатор является уникальным, я могу получить эту функциональность уродливым способом, выполнив обновление с сопоставлением фильтра если идентификатор и версия меньше новой версии документа, а затем перехватывать исключение при попытке вставить конфликтующий документ в случае, когда сохраненный документ имеет ту же или более позднюю версию.
var idMatchFilter = Builders.Filter.Eq(e => e.Id, upsertDocument.Id);
var versionFilter = Builders.Filter.Lt(e => e.Version, upsertDocument.Version);
var combinedFilter = Builders.Filter.And(idMatchFilter, versionFilter);

var replaceOptions = new ReplaceOptions() { IsUpsert = true };
try
{
await testCollection.ReplaceOneAsync(combinedFilter, upsertDocument, replaceOptions);
}
catch (MongoWriteException mongoWriteException) when (mongoWriteException.Message.Contains("A write operation resulted in an error. WriteError: { Category : \"DuplicateKey\", Code : 11000, Message : \"E11000 duplicate key error collection"))
{
// expected possible error when we attempt to insert an older document - ideally this would be a no-op instead of attempted write
}

Однако, поскольку фактический вариант использования представляет собой массовое обновление (скажем, 1000 документов) гораздо более сложного документа (неважно, где мы могли бы просто установить одно или два свойства, нам нужно заменить документ), то это не кажется хорошим подходом, если это вообще осуществимо. Это также противоречит Azure Cosmo DB, хотя я надеюсь, что это не будет иметь смысла, и все, что работает с обычным mongodb, должно работать и здесь.
Вот тест xunit, который включает в себя некоторые из вещи, которые я пробовал, и почему они до сих пор терпели неудачу. Обратите внимание, что в том виде, в каком оно написано, оно проходит, но только потому, что мы перехватываем это исключение MongoWriteException, и цель состоит в том, чтобы выполнить операцию, которая просто не выполняет никаких операций, а не пытается записать документ старой версии.
[Theory]
// insert, nothing persisted yet
[InlineData(null, 1, true)]
// update, persisted version is older
[InlineData(1, 2, true)]
// update, persisted version is same
[InlineData(2, 2, false)]
// update, persisted version is newer
[InlineData(2, 1, false)]
public async Task ConditionalUpsert(int? persistedVersion, int upsertVersion, bool shouldUpdate)
{
// arrange
var testCollection = GetTestCollection();
var emptyFilter = Builders.Filter.Empty;
await testCollection.DeleteManyAsync(emptyFilter);
if (persistedVersion.HasValue)
{
// seed the collection with expected version
var testDocument = new TestDocument("Foo", persistedVersion.Value, "persisted document dummy payload");
await testCollection.InsertOneAsync(testDocument);
}
var upsertDocument = new TestDocument("Foo", upsertVersion, "new document dummy payload");

// act
var idMatchFilter = Builders.Filter.Eq(e => e.Id, upsertDocument.Id);
var versionFilter = Builders.Filter.Lt(e => e.Version, upsertDocument.Version);
var combinedFilter = Builders.Filter.And(idMatchFilter, versionFilter);

// incorrectly updates the persisted document when trying to write older version
//var findOneAndReplaceOptions = new FindOneAndReplaceOptions { IsUpsert = true };
//await testCollection.FindOneAndReplaceAsync(idMatchFilter, upsertDocument, findOneAndReplaceOptions);

// incorrectly tries to insert new document insert of no-op when upserting an old version
// Command findAndModify failed: E11000 duplicate key error collection: SomeDatabase.SomeCollection. Failed _id or unique index constraint
//var findOneAndReplaceOptions = new FindOneAndReplaceOptions { IsUpsert = true };
//await testCollection.FindOneAndReplaceAsync(combinedFilter, upsertDocument, findOneAndReplaceOptions);

var replaceOptions = new ReplaceOptions() { IsUpsert = true };
try
{
// incorrectly tries to insert new document with older version since the specified filter doesn't find the persisted version if it was newer
await testCollection.ReplaceOneAsync(combinedFilter, upsertDocument, replaceOptions);
}
catch (MongoWriteException mongoWriteException) when (mongoWriteException.Message.Contains("A write operation resulted in an error. WriteError: { Category : \"DuplicateKey\", Code : 11000, Message : \"E11000 duplicate key error collection"))
{
// expected possible error when we attempt to insert an older document - ideally this would be a no-op instead of attempted write
}

// assert
var allDocuments = await testCollection.Find(emptyFilter).ToListAsync();
var queriedDocument = Assert.Single(allDocuments);
var expectedVersion = shouldUpdate ? upsertVersion : persistedVersion;
Assert.Equal(expectedVersion, queriedDocument.Version);
}


Подробнее здесь: https://stackoverflow.com/questions/790 ... -collectio
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»