使用官方C#驱动程序在Mongo DB中进行Upsert操作

52
在MongoDB的官方文档中提到了upserts,因此编写upsert命令比编写以下命令更好:
if (_campaignRepo.Exists(camp))
{
    _campaignRepo.DeleteByIdAndSystemId(camp);
}

_campaignRepo.Save(camp);

如果可能的话,希望有一种方法在数据库层面实现这种逻辑。那么,如果有一种方法进行upsert操作,该如何实现呢?

4个回答

83

MongoDB C# driver 的第二个版本需要在写入命令中设置IsUpsert标志。这个示例将 upsert 整个文档。

var newDoc = new BsonDocument { { "_id", 123 }, { "someKey", "someValue" } };
var result = await collection.ReplaceOneAsync(
                filter: new BsonDocument("_id", 123),
                options: new ReplaceOptions { IsUpsert = true },
                replacement: newDoc);

MongoDB C# driver 版本1Save命令中实现了这个逻辑。

var newDoc = new BsonDocument { { "_id", 123 }, { "someKey", "someValue" } };
collection.Save(newDoc);
Save方法是Insert和Update的组合。如果文档的Id成员有值,则认为它是一个现有的文档,Save在文档上调用Update(设置Upsert标志以防它实际上是一个新文档)。否则,它被视为新文档,并且在首先为Id成员分配一个新生成的唯一值后,Save调用Insert。
参考:http://mongodb.github.io/mongo-csharp-driver/1.11/driver/#save-tdocument-method 注意:这确实需要正确映射Id字段。有关更多信息,请参见:http://mongodb.github.io/mongo-csharp-driver/1.11/serialization/#identifying-the-id-field-or-property

1
使用新的API已经不再可能了。 - Asad Saeeduddin
1
更新了。虽然我认为新的API使得upsert命令更难使用了。 - Mani Gandham
1
如果newDocs ID为空,即插入操作,那么会传递什么给过滤器?只是一个空的BSON文档吗? - BenCr
1
@BenCr 如果你只是进行普通的插入而不是 upsert,那么你应该使用 InsertOneAsync 方法。这不需要指定 ID 字段,因为 MongoDB 会在插入期间创建一个,但你也可以像 GUID 一样在不访问数据库的情况下创建这些 ID,所以这取决于你。更多信息请参见 C# 驱动程序文档:http://mongodb.github.io/mongo-csharp-driver/2.1/reference/driver/crud/writing/ - Mani Gandham

44

从v2.0版本的驱动程序开始,有一个新的仅支持异步的API。旧的API不应再被使用,因为它是新API上的阻塞外观,已经过时。

目前推荐的更新文档的方法是调用并等待ReplaceOneAsync,并开启IsUpsert标志和匹配相关文档的筛选器:

Hamster hamster = ...
var replaceOneResult = await collection.ReplaceOneAsync(
    doc => doc.Id == hamster.Id, 
    hamster, 
    new UpdateOptions {IsUpsert = true});

通过查看ReplaceOneResult.MatchedCount,您可以检查操作是插入还是更新:


1
我正在尝试使用ReplaceOneAsync,但它抛出异常:“元素名称' '无效”,我正在使用过滤器进行updateOneasync,它可以工作,但是replaceone不行。你能帮我吗? - sifa vahora
6
UpdateOptions已弃用,应改为使用new ReplaceOptions { IsUpsert = true }); - Alde
我遇到了这个错误 - 表达式不支持:doc.Id in (doc.Id == "4222460e-eabc-446c-a4d9-51504f9f25ba"),因为字段"_id"没有表示为字符串。 - Varun Sharma
好的,如果将此属性从Id字段中移除,它就能正常工作。 - Varun Sharma

38
以下代码摘自一个正常运行的应用程序:
weekplanStore.Update(
    Query.EQ("weekNumber", week),
    Update.Replace(rawWeekPlan),
    UpdateFlags.Upsert);

weekplanStore是我的MongoDB集合,代码会更新找到的第一个参数中的文档,如果没有找到,则插入一个新文档。 "技巧"在于使用UpdateFlags.Upsert修饰符。

rawWeekPlan是被插入或更新的对象,其类型如下:

private class RawWeekPlan
{
    public ObjectId id;
    public int weekNumber;
    public WeekPlanEntry[] entries;
}

并由驱动程序自动转换为BSON格式。


1
它对嵌套集合起作用吗?你试过了吗?因为在我的情况下,它对它们不起作用。 - Yurii Hohan

6
您可以使用常规的更新命令,但只需传递Upsert更新标志即可。
MongoCollection collection = db.GetCollection("matches");
var query = new QueryDocument("recordId", recordId);

var update = Update.Set("FirstName", "John").Set("LastName","Doe");
matchCollection.Update(query, update, UpdateFlags.Upsert, SafeMode.False);

那段代码是从一个可运行的应用程序中改编而来的(为了更加清晰,进行了缩短)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接