gpt4 book ai didi

c# - 带有 Guid 的 MongoDB C# Upsert

转载 作者:可可西里 更新时间:2023-11-01 10:31:37 25 4
gpt4 key购买 nike

当尝试在 Mongo 中执行更新插入操作时,我想让它为 ID 而不是对象 ID 生成一个 GUID。在这种情况下,我正在检查以确保具有特定属性的对象尚不存在,并且在发生更新时实际抛出异常。

这是类定义的 stub :

public class Event 
{
[BsonId(IdGenerator = typeof(GuidGenerator) )]
[BsonRepresentation(BsonType.String)]
[BsonIgnoreIfDefault]
public Guid Id { get; set; }

// ... more properties and junk
}

下面是我们如何执行更新插入操作:

// query to see if there are any pending operations
var keyMatchQuery = Query<Event>.In(r => r.Key, keyList);
var statusMatchQuery = Query<Event>.EQ(r => r.Status, "pending");
var query = Query.And(keyMatchQuery , statusMatchQuery );

var updateQuery = new UpdateBuilder();
var bson = request.ToBsonDocument();

foreach (var item in bson)
{
updateQuery.SetOnInsert(item.Name, item.Value);
}

var fields = Fields<Request>.Include(req => req.Id);

var args = new FindAndModifyArgs()
{
Fields = fields,
Query = query,
Update = updateQuery,
Upsert = true,
VersionReturned = FindAndModifyDocumentVersion.Modified
};

// Perform the upsert
var result = Collection.FindAndModify(args);

这样做会将 ID 生成为 ObjectID 而不是 GUID。

我绝对可以通过先执行 .FindOne 来获得我想要的两步操作行为,如果失败,则直接插入:

var existingItem = Collection.FindOneAs<Event>(query);

if (existingItem != null)
{
throw new PendingException(string.Format("Event already pending: id={0}", existingItem.Id));
}

var result = Collection.Insert(mongoRequest);

在这种情况下,它为新项目正确设置了 GUID,但操作是非原子的。我正在寻找一种在驱动程序级别设置默认 ID 生成机制的方法,并认为这样做可以:

BsonSerializer.RegisterIdGenerator(typeof(Guid), GuidGenerator.Instance);

...但无济于事,我认为这是因为对于更新插入,无法包含 ID 字段,因此没有序列化发生,Mongo 正在完成所有工作。我还研究了实现一个约定,但这没有意义,因为有单独的生成机制来处理它。有没有我应该考虑的不同方法和/或我只是遗漏了什么?

我确实意识到 GUID 在 Mongo 中并不总是理想的,但由于与其他系统的兼容性,我们正在探索使用它们。

最佳答案

发生的事情是只有服务器知道 FindAndModify 是否最终会成为更新插入,并且按照目前的写法,它是服务器自动生成 _id 值,并且服务器只能假设 _id值应该是一个 ObjectId(服务器对您的类声明一无所知)。

这是一个使用 shell 显示您的场景的简化示例(减去所有 C# 代码...):

> db.test.drop()
> db.test.find()
> var query = { x : 1 }
> var update = { $setOnInsert : { y : 2 } }
> db.test.findAndModify({ query: query, update : update, new : true, upsert : true })
{ "_id" : ObjectId("5346c3e8a8f26cfae50837d6"), "x" : 1, "y" : 2 }
> db.test.find()
{ "_id" : ObjectId("5346c3e8a8f26cfae50837d6"), "x" : 1, "y" : 2 }
>

我们知道这是一个更新插入,因为我们在空集合上运行了它。请注意,服务器使用查询作为新文档的初始模板(这是“x”的来源),应用了更新规范(这是“y”的来源),并且因为文档没有“_id”它为其生成了一个新的 ObjectId。

诀窍是生成 _id 客户端,以防万一它被证明是需要的,但是将它放在更新规范中,使其仅在它是新文档时才适用。这是前面使用 $setOnInsert 作为 _id 的例子:

> db.test.drop()
> db.test.find()
> var query = { x : 1 }
> var update = { $setOnInsert : { _id : "E3650127-9B23-4209-9053-1CD989AE62B9", y : 2 } }
> db.test.findAndModify({ query: query, update : update, new : true, upsert : true })
{ "_id" : "E3650127-9B23-4209-9053-1CD989AE62B9", "x" : 1, "y" : 2 }
> db.test.find()
{ "_id" : "E3650127-9B23-4209-9053-1CD989AE62B9", "x" : 1, "y" : 2 }
>

现在我们看到服务器使用了我们提供的 _id 而不是生成 ObjectId。

就您的 C# 代码而言,只需将以下内容添加到您的 updateQuery 中:

updateQuery.SetOnInsert("_id", Guid.NewGuid().ToString());

您应该考虑将 updateQuery 变量重命名为 updateSpecification(或只是更新),因为从技术上讲它不是查询。

不过有一个问题...此技术仅适用于当前的 2.6 版本的服务器。请参阅:https://jira.mongodb.org/browse/SERVER-9958

关于c# - 带有 Guid 的 MongoDB C# Upsert,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22943670/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com