猿教程 Logo

.Net连接MongoDb:使用MongoDb驱动执行更新操作

介绍

在上一篇文章中,我们讨论了如何通过MongoDb .NET驱动程序插入新文档。 我们看到使用各种Insert方法并不是特别复杂。 我们只需要提供要插入的对象即可。

在这篇文章中,我们将介绍如何更新文档。 我们已经看到如何通过Mongo shell进行更新,现在已经足够了解它们在C#中的表现。

回想一下,我们将更新分为两类:替换匹配文档为全新的更新,或匹配文档被修改而不被替换的“真”更新。 请记住,即使文档被替换,_id字段也不能被修改。

更新在驱动程序中有许多形式,但也可以分为两类。 让我们依次看看他们。

替换

替换由以下功能表示:

  • ReplaceOne:用FilterDefinition搜索语法替换文档

  • ReplaceOne扩展方法:用Linq表达式搜索语法替换文档

  • ReplaceOneAsync:等待版本的ReplaceOne

  • ReplaceOneAsync扩展方法:等价版的ReplaceOne扩展方法

  • FindOneAndReplace:此操作查找文档并以原子替换

  • FindOneAndReplaceAsync:等待版本的FindOneAndReplace

我们来看一个带有FilterDefinition的ReplaceOne函数的例子。 同时我们还会看到如何通过我们可以传入函数的选项来使用Upserts。 回想一下,我们在上一篇文章中插入了几家假餐馆。 一个是墨西哥菜的墨西哥餐厅。 我们用新的东西替代它:

ModelContext modelContext = ModelContext.Create(new ConfigFileConfigurationRepository(), new AppConfigConnectionStringRepository());
RestaurantDb mexicanReplacement = new RestaurantDb();
mexicanReplacement.Address = new RestaurantAddressDb()
{
	BuildingNr = "3/D",
	Coordinates = new double[] { 24.68, -100.9 },
	Street = "Mexico Street",
	ZipCode = 768324865
};
mexicanReplacement.Borough = "Somewhere in Mexico";
mexicanReplacement.Cuisine = "Mexican";
mexicanReplacement.Grades = new List<RestaurantGradeDb>()
{
	new RestaurantGradeDb() {Grade = "B", InsertedUtc = DateTime.UtcNow, Score = "10" },
	new RestaurantGradeDb() {Grade = "B", InsertedUtc = DateTime.UtcNow, Score = "4" }
};
mexicanReplacement.Id = 457656745;
mexicanReplacement.Name = "NewMexicanKing";
mexicanReplacement.MongoDbId = ObjectId.Parse("571fc8d290f5282a50778960");
ReplaceOneResult replaceOneResult = modelContext.Restaurants.ReplaceOne(Builders<RestaurantDb>.Filter.Eq(r => r.Name, "MexicanKing"), mexicanReplacement, new UpdateOptions() { IsUpsert = true });
Console.WriteLine(replaceOneResult.IsAcknowledged);
Console.WriteLine(replaceOneResult.MatchedCount);
Console.WriteLine(replaceOneResult.ModifiedCount);
Console.WriteLine(replaceOneResult.UpsertedId);

这里有几件事要注意:

  • 我事先知道会有一个匹配的文件将被替换。 因此,我必须提供与MexicanKing限制器相同的对象ID,否则驱动程序将抛出一个异常,表示不能覆盖_id。 这是为什么? 如果我们没有在替换文档中提供一个ID字段,那么它将为我们创建,而ReplaceOne函数会“想”我们真的想要更改对象ID

  • 因此,以这种方式使用ReplaceOne并不容易。 实际上,我们首先搜索一个现有的文档,将其属性映射到替换文档,然后用代码修改替换文档,然后将其传递给ReplaceOne函数。 这样我们就不会丢失任何对象属性,也不会使驱动程序生气。

  • 我们从函数返回一个ReplaceOneResult对象,这有助于我们检查替换操作是否成功:操作是否已被确认,有多少个文档被匹配和修改 - 在这种情况下,这些文件可以是0或1,如果upsert导致 在插入中,我们可以读取插入的文档的ID。 在这种情况下,后一个字段将显然为空

除了它接受搜索功能的LINQ表达式之外,ReplaceOne扩展方法的工作方式大致相同。 我们已经看到了足够的那些,所以我不会在这里显示一个例子。

让我们来看看更加精彩的FindOneAndReplace方法。 以下是使用通用FindAndReplaceOptions对象的示例:

ModelContext modelContext = ModelContext.Create(new ConfigFileConfigurationRepository(), new AppConfigConnectionStringRepository());
RestaurantDb mexicanReplacement = new RestaurantDb();
mexicanReplacement.Address = new RestaurantAddressDb()
{
	BuildingNr = "4/D",
	Coordinates = new double[] { 24.68, -100.9 },
	Street = "New Mexico Street",
	ZipCode = 768324865
};
mexicanReplacement.Borough = "In the middle of Mexico";
mexicanReplacement.Cuisine = "Mexican";
mexicanReplacement.Grades = new List<RestaurantGradeDb>()
{
	new RestaurantGradeDb() {Grade = "B", InsertedUtc = DateTime.UtcNow, Score = "10" },
	new RestaurantGradeDb() {Grade = "B", InsertedUtc = DateTime.UtcNow, Score = "4" }
};
mexicanReplacement.Id = 457656745;
mexicanReplacement.Name = "BrandNewMexicanKing";
mexicanReplacement.MongoDbId = ObjectId.Parse("571fc8d290f5282a50778960");
		
RestaurantDb replaced = modelContext.Restaurants.FindOneAndReplace
	(Builders<RestaurantDb>.Filter.Eq(r => r.Name, "NewMexicanKing"), 
	mexicanReplacement, 
	new FindOneAndReplaceOptions<RestaurantDb, RestaurantDb>()
	{
		IsUpsert = true,
		ReturnDocument = ReturnDocument.After,
		Sort = Builders<RestaurantDb>.Sort.Descending(r => r.Name)
	});

如上所述,这是一个包含搜索和替换的原子操作。 您将识别前两个参数,即过滤器定义和替换文档。 FindOneAndReplaceOptions对象更有趣。 请注意,这是一个可选的输入,默认为null,但它提供了几个有趣的选项:

  • IsUpsert:我们现在已经熟悉了

  • ReturnDocument:可以在之后或之前。 此选项指定从替换操作返回的文档:替换的文档(即Before)或替换现有文档(即After)的文档。 如果我们要在更换操作返回后再进行额外的检查,这将非常有用

  • 排序:正如函数名称所示,即使有很多匹配搜索条件,我们也会找到一个文档。 我们没有太多的控制MongoDb将找到哪个文档,但Sort选项至少提供一些控件。 Sort接受我们前面看到的排序定义,意味着该函数应该以给定的排序顺序替换第一个文档。 在示例中,匹配的文档将按名称按降序排序,并且该排序顺序中的第一个文档将被替换

您会注意到,FindOneAndReplaceOptions是通用的,并接受文档类型和要投影的类型。 最常见的两种类型将是相同的,但有时您可能希望将数据库文档类型映射到不同的类型。

更新

更新由驱动程序中的函数列表表示:

  • UpdateOne:ReplaceOne的更新“brother”。 它接受过滤器定义和更新定义

  • UpdateOne扩展方法:与UpdateOne相同,但使用LINQ表达式来过滤文档

  • UpdateOneAsync:UpdateOne的异步版本

  • UpdateMany:与UpdateOne相同,但它会更新所有匹配的文档

  • UpdateMany扩展名:与UpdateMany相同,但使用LINQ表达式

  • UpdateManyAsync:UpdateMany的等待版本

  • UpdateManyAsync扩展名:UpdateMany扩展方法的等待版本

  • FindOneAndUpdate:与FindOneAndReplace相同但具有更新定义对象

  • FindOneAndUpdateAsync:等待版本的FindOneAndUpdate

所以我们在这里有很长的选择。 他们的工作方式与他们的替代品相同。 更新定义构建器是新的,它暴露了我们之前看过的更新运算符,例如$ set,$ inc,$ addToSet。 它还暴露了我们之前看到的Combine方法,并接受更新定义的列表。 这有助于我们在单个文档上构建多个更新操作。

我们来看一个使用UpdateOne的例子,其中我们为Borough属性设置一个新值,并将一个新的成绩推送到成绩集合:

ModelContext modelContext = ModelContext.Create(new ConfigFileConfigurationRepository(), new AppConfigConnectionStringRepository());
var updateDefinitionBorough = Builders<RestaurantDb>.Update.Set(r => r.Borough, "New Borough");
var updateDefinitionGrades = Builders<RestaurantDb>.Update.Push(r => r.Grades, new RestaurantGradeDb() { Grade = "A", InsertedUtc = DateTime.UtcNow, Score = "6" });
var combinedUpdateDefinition = Builders<RestaurantDb>.Update.Combine(updateDefinitionBorough, updateDefinitionGrades);
UpdateResult updateResult = modelContext.Restaurants.UpdateOne(r => r.Name == "BrandNewMexicanKing", combinedUpdateDefinition, new UpdateOptions() { IsUpsert = true });

现在你会认识到Builder。 它公开一个Update属性来构建更新定义。 如果在Visual Studio中键入“更新”,IntelliSense将会显示以前熟悉的许多选项,例如$ set和$ push,您会发现所有这些选项。 接下来,我们组合更新定义,以便两个更新将应用于匹配的文档。 最后我们调用UpdateOne,这次用LINQ表达式搜索。 UpdateResult对象公开了与上面的ReplaceOneResult完全相同的属性,所以我不再重复。

UpdateMany函数以完全相同的方式应用。 不同之处在于更新定义将应用于所有匹配的文档。

此外,FindOneAndUpdate与FindOneAndReplace几乎相同,只是它接受过滤器定义,更新定义和FindOneAndUpdateOptions对象。 后者具有与FindOneAndReplaceOptions相同的属性,因此您可以轻松地找出如何在代码中使用它。

在下一篇文章中,我们将看看删除内容。


版权声明:本站所有教程均为本站原创或翻译,转载请注明出处,请尊重他人劳动果实。请记住本站地址:www.yuanjiaocheng.net (猿教程) 作者:卿文刚
本文标题: C#环境
本文地址:http://www.yuanjiaocheng.net/CsharpMongo/19.html