Как преобразовать .NET GUID в ObjectID объекта MongoDB (в С#). Кроме того, можно ли преобразовать его обратно в тот же идентификатор GUID из ObjectID?
Преобразование .NET Guid в ObjectID объекта MongoDB
Ответ 1
Вы не можете преобразовать ObjectId
в GUID
и наоборот, потому что это две разные вещи (разные размеры, алгоритмы).
Вы можете использовать любой тип для mongoDb _id
включая GUID
.
Например, в официальном С#-драйвере вы должны указать атрибут [BsonId]
:
[BsonId]
public Guid Id {get;set;}
[BsonId]
public int Id {get;set;}
Объектный идентификатор BSON - это 12-байтовое значение состоящий из 4-байтовой метки времени (секунды с эпохи), 3-байтовый идентификатор машины, 2-байтовый идентификатор процесса и 3-байтовый счетчик. Обратите внимание, что временные метки и поля счетчика должны быть в отличие от остальных BSON. Это потому, что они побайтовый байт, и мы хотим обеспечить в основном возрастающий порядок.
GUID:
Значение GUID представляется как 32-символьная шестнадцатеричная строка, такие как {21EC2020-3AEA-1069-A2DD-08002B30309D}, и обычно хранится как 128-битный целое число
Ответ 2
FYI Вы можете конвертировать из ObjectId в Guid
public static Guid AsGuid(this ObjectId oid)
{
var bytes = oid.ToByteArray().Concat(new byte[] { 5, 5, 5, 5 }).ToArray();
Guid gid = new Guid(bytes);
return gid;
}
/// <summary>
/// Only Use to convert a Guid that was once an ObjectId
/// </summary>
public static ObjectId AsObjectId(this Guid gid)
{
var bytes = gid.ToByteArray().Take(12).ToArray();
var oid = new ObjectId(bytes);
return oid;
}
Ответ 3
хотя и не прямой ответ, имейте в виду, что нет требования о том, что _id является объектным идентификатором --- только, чтобы он был уникальным.
любой допустимый тип может быть установлен для _I, включая встроенный объект или a. вы должны быть в порядке (запрещаете любые нарушения уникальности) с помощью GUID для _id; на самом деле, ObjectID действительно просто пользовательский GUID.
Ответ 4
Если вы начинаете все с нуля, вы можете ввести свой элемент Id как Guid
вместо ObjectId
. Это предпочтительнее, потому что тогда ваша модель не должна ссылаться на MongoDB.Bson
, что делает его, вероятно, классом POCO. Вам даже не нужен [BsonId]
если вы называете элемент "Id", и лучше этого не делать по вышеупомянутой причине.
Если вы уже застряли с использованием ObjectId
в ваших классах POCO и - осознав трудности - хотели бы изменить тип "Id" (в вашем классе), но не можете изменить тип "_id" ( в ваших данных), вы можете сделать собственный сериализатор:
public class SafeObjectIdSerializer: ObjectIdSerializer
{
public SafeObjectIdSerializer() : base() { }
public override ObjectId Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var bsonReader = context.Reader;
var bsonType = bsonReader.GetCurrentBsonType();
switch (bsonType)
{
case BsonType.Binary: {
var value = bsonReader
.ReadBinaryData()
.AsGuid
.ToString()
.Replace("-", "")
.Substring(0, 24);
return new ObjectId(value);
}
}
return base.Deserialize(context, args);
}
}
Как упомянул MiddleTommy, переход от Guid
к ObjectId
с потерями, но в зависимости от того, как вы используете это поле, это может не быть проблемой. Выше используются первые 24 шестнадцатеричных символа и отбрасываются оставшиеся 8. Если вы храните случайные значения ObjectId
а не, скажем, преобразование ObjectId в инкрементные целые числа, у вас все должно быть в порядке.
Если вы хотите также начать писать ObjectId
как Guid
, похоже, ничто не мешало бы смешивать типы "_id", если вы откатываетесь на base.Deserialize()
, но я могу ошибаться, и это может иметь значение в зависимости от вашей реализации. Документация не говорит так или иначе. Для этого вы можете добавить это переопределение в вышеприведенный класс:
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, ObjectId value)
{
var bsonWriter = context.Writer;
var guidString = value
.ToString()
.Insert(8, "-")
.Insert(13, "-")
.Insert(18, "-")
.Insert(23, "-") + "00000000";
var asGuid = new Guid(guidString);
bsonWriter.WriteBinaryData(new BsonBinaryData(asGuid));
}
Чтобы сделать это вашим глобальным десериализатором:
public class CustomSerializationProvider : IBsonSerializationProvider
{
public IBsonSerializer GetSerializer(Type type)
{
if (type == typeof(ObjectId))
{
return new SafeObjectIdSerializer();
}
//add other custom serializer mappings here
//to fall back on the default:
return null;
}
}
Затем где-нибудь, где он будет вызываться только один раз, как ваш Global.asax Application_Start()
BsonSerializer.RegisterSerializationProvider(new CustomSerializationProvider());