【发布时间】:2011-12-21 04:14:36
【问题描述】:
在通过官方c#驱动程序将新文档插入mongodb后,如何立即读回生成的_id,以便我可以将其用作其他集合的“外来”键?我知道在 sql server 中我可以立即读回新插入行的标识列值,所以我需要 mongodb 中的类似功能。
由于 mongo 生成的 _id 不是对象的实际成员,假设您需要对通用 bsondocument 做一些事情?
【问题讨论】:
在通过官方c#驱动程序将新文档插入mongodb后,如何立即读回生成的_id,以便我可以将其用作其他集合的“外来”键?我知道在 sql server 中我可以立即读回新插入行的标识列值,所以我需要 mongodb 中的类似功能。
由于 mongo 生成的 _id 不是对象的实际成员,假设您需要对通用 bsondocument 做一些事情?
【问题讨论】:
您可以使用 findAndModify 命令执行 upsert 以实现同样的效果,而工作量比生成您自己的 id 少。 (何必呢,10gen 决定使用的场景有一个很好的理由——它可以轻松进行分片)
findAndModify 命令可让您查找或更新插入(如果不存在则创建)文档并返回该文档。
一般形式如下:
db.runCommand( { findAndModify : <collection>, <options> } )
您可以阅读更多关于它的信息here。
除了 upsert 选项之外,您还想使用 new 来取回新创建的对象。
【讨论】:
如果需要_id,可以自己生成,在文档上手动设置。
【讨论】:
在 MongoDB 中,ID(通常)是在客户端生成的。您可以自己生成一个,使用适当的驱动程序调用,放入文档中并保存。
我没有使用 C# 驱动程序,但 Ruby 驱动程序为我完成了所有工作。
ruby-1.9.3-p0 :027 > obj = coll.insert({'foo' => 'bar'})
=> BSON::ObjectId('4ef15e7f0ed4c00272000001')
ruby-1.9.3-p0 :030 > coll.find.to_a
=> [{"_id"=>BSON::ObjectId('4ef15e7f0ed4c00272000001'), "foo"=>"bar"}]
这就是我可以制作新 ID 的方法
ruby-1.9.3-p0 :039 > newid = BSON::ObjectId.new
=> BSON::ObjectId('4ef15f030ed4c00272000002')
ruby-1.9.3-p0 :040 > coll.insert({_id: newid, test: 'test'})
=> BSON::ObjectId('4ef15f030ed4c00272000002')
ruby-1.9.3-p0 :041 > coll.find.to_a
=> [{"_id"=>BSON::ObjectId('4ef15e7f0ed4c00272000001'), "foo"=>"bar"}, {"_id"=>BSON::ObjectId('4ef15f030ed4c00272000002'), "test"=>"test"}]
【讨论】:
在大多数驱动程序中,_id 字段实际上是在客户端生成的,然后才转到服务器。 MongoDB 不使用“自动递增”ID,因此您实际上可以生成一个随机 ID 并告诉服务器“使用它”。
在 C# 中,代码如下所示:
var id = ObjectId.GenerateNewId();
因此您可以创建一个 BSON 文档并简单地保存它:
var toSave = new BsonDocument {
{ "_id", ObjectId.GenerateNewId() },
{ "data", "my data" }
};
db.collection.Save(toSave);
但是,默认情况下,当您 .Save() 一个文档时,这将更新 _id 字段。所以你通常可以只保存BSONDocument(或BSONSerializable类)然后再读回来。
请注意,有一个名为 DBRef 的规范有助于简化“外键”的实现。文档是 here,在 C# 中,您需要查看 DBRef 类。
【讨论】:
就像这里的其他答案所说,ID 是在客户端分配的。您可以做的事情是创建一个默认值约定,如果尚未设置,则在插入期间生成一个新 ID。
public class DefaultValueConvention : MongoDB.Bson.Serialization.Conventions.IDefaultValueConvention
{
public object GetDefaultValue(MemberInfo memberInfo)
{
var type = memberInfo.MemberType == MemberTypes.Property
? ((PropertyInfo) memberInfo).PropertyType
: ((FieldInfo) memberInfo).FieldType;
if (type.IsSubclassOf(typeof(ObjectId)))
return ObjectId.GenerateNewId();
else
return null;
}
}
并设置驱动程序以使用此约定:
var profile = new ConventionProfile();
profile.SetDefaultValueConvention(new DefaultValueConvention());
BsonClassMap.RegisterConventions(profile, x => x.FullName.StartsWith("ConsoleApplication"));
所以现在您可以创建一个对象并将其保存在 2 行中:
var animal = new Animal {Name = "Monkey", PercentDeviationFromHumans = 2.01};
db["animals"].Save(animal);
实际上,对于最新的驱动程序,您甚至不需要设置默认值约定,它已经具有这种行为 OOTB。无论如何,mongo 中的约定都没有得到充分利用。
【讨论】: