简体   繁体   中英

Convert .NET Guid to MongoDB ObjectID

How can I convert a .NET GUID to a MongoDB ObjectID (in C#). Also, can I convert it back again to the same GUID from the ObjectID?

You can't convert ObjectId into GUID and vice versa, because they are two different things(different sizes, algoritms).

You can use any type for mongoDb _id including GUID .

For example in official c# driver you should specify attribute [BsonId] :

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

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

ObjectId :

A BSON ObjectID is a 12-byte value consisting of a 4-byte timestamp (seconds since epoch), a 3-byte machine id, a 2-byte process id, and a 3-byte counter. Note that the timestamp and counter fields must be stored big endian unlike the rest of BSON. This is because they are compared byte-by-byte and we want to ensure a mostly increasing order.

GUID :

The value of a GUID is represented as a 32-character hexadecimal string, such as {21EC2020-3AEA-1069-A2DD-08002B30309D}, and is usually stored as a 128-bit integer

FYI You can convert from an ObjectId to a 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;
    }

although not a direct answer keep in mind that there is no.requirement that _id be an ObjectID --- only that it be unique.

any valid type can be set for _I'd including an embedded object or a . you should be fine (barring any uniqueness violations) using a GUID for _id; in fact, ObjectID is really just a custom GUID.

If you're starting everything from scratch, you can type your "Id" member as a Guid instead of ObjectId . That's preferred because then your model doesn't have to reference MongoDB.Bson , arguably making it a POCO class no more. You don't even need the [BsonId] attribute if you name the member "Id", and it's better not to for the aforementioned reason.

If you're already stuck using ObjectId in your POCO classes and--having realized the difficulties--would like to change the type of "Id" (in your class), but are not able to change the type of "_id" (in your data), you can make a custom serializer:

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);
    }
}

As MiddleTommy mentioned, going from Guid to ObjectId is lossy, but depending on how you use that field, that may not be a problem. The above uses the first 24 hexadecimal characters and discards the remaining 8. If you're storing random ObjectId values and not say, an ObjectId conversion of incrementing ints, you should be fine.

If you want to also start writing ObjectId as Guid , it doesn't seem to hurt anything to mix "_id" types, as long as you're falling back on base.Deserialize() , but I could be wrong and it could matter depending on your implementation. The documentation doesn't say one way or the other. To do that you can add this override to the above class:

    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));
    }

To make that your global deserializer:

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;
    }
}

Then somewhere where it will only be called once, like your Global.asax Application_Start()

BsonSerializer.RegisterSerializationProvider(new CustomSerializationProvider());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM