Конвертировать .NET Guid в MongoDB ObjectID - PullRequest
25 голосов
/ 01 апреля 2011

Как я могу преобразовать .NET GUID в объектный идентификатор MongoDB (в C #).Кроме того, я могу преобразовать его обратно в тот же GUID из ObjectID?

Ответы [ 4 ]

32 голосов
/ 01 апреля 2011

Вы не можете конвертировать ObjectId в GUID и наоборот, потому что это две разные вещи (разные размеры, алгоритмы).

Вы можете использовать любой тип для mongoDb _id, включая GUID.

Например, в официальном драйвере c # вы должны указать атрибут [BsonId]:

[BsonId]
public Guid Id {get;set;}

[BsonId]
public int Id {get;set;}

ObjectId :

ABSON ObjectID представляет собой 12-байтовое значение, состоящее из 4-байтовой метки времени (секунды с начала эпохи), 3-байтового идентификатора компьютера, 2-байтового идентификатора процесса и 3-байтового счетчика.Обратите внимание, что поля timestamp и counter должны храниться с прямым порядком байтов в отличие от остальной части BSON.Это связано с тем, что они сравниваются побайтово, и мы хотим обеспечить максимально увеличивающийся порядок.

GUID :

ЗначениеGUID представлен в виде шестнадцатеричной строки из 32 символов, например {21EC2020-3AEA-1069-A2DD-08002B30309D}, и обычно хранится в виде 128-разрядного целого числа

6 голосов
/ 12 марта 2013

К вашему сведению Вы можете конвертировать из 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 голосов
/ 01 апреля 2011

хотя это и не прямой ответ, имейте в виду, что нет требования, чтобы _id было ObjectID --- только чтобы оно было уникальным.

Любой допустимый тип может быть установлен для _I'd, включая встроенный объект или. у вас должно быть все в порядке (исключая любые нарушения уникальности), используя GUID для _id; фактически ObjectID - это просто пользовательский GUID.

0 голосов
/ 08 июня 2019

Если вы начинаете все с нуля, вы можете ввести свой элемент "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());
...