Подтвердить что ты не робот

Преобразование .NET Guid в ObjectID объекта MongoDB

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

4b9b3361

Ответ 1

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

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

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

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

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

ObjectId:

Объектный идентификатор 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());