web-dev-qa-db-ja.com

.NET GuidをMongoDB ObjectIDに変換する

.NET GUIDをMongoDB ObjectIDに(C#で)変換する方法を教えてください。また、ObjectIDから同じGUIDに再度変換することはできますか? ?

26
Journeyman

ObjectIdGUIDに、またはその逆に変換することはできません。これらは2つの異なるもの(異なるサイズ、アルゴリズム)だからです。

MongoDb _idには、GUIDを含む任意のタイプを使用できます。

たとえば、公式のc#ドライバーでは、属性[BsonId]を指定する必要があります。

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

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

ObjectId

BSON ObjectIDは、4バイトのタイムスタンプ(エポックからの秒数)、3バイトのマシンID、2バイトのプロセスID、および3バイトのカウンターで構成される12バイトの値です。タイムスタンプとカウンタフィールドは、BSONの他の部分とは異なり、ビッグエンディアンで格納する必要があることに注意してください。これは、それらがバイトごとに比較され、ほとんど増加する順序を保証したいためです。

[〜#〜] guid [〜#〜]

GUID=の値は、{21EC2020-3AEA-1069-A2DD-08002B30309D}などの32文字の16進文字列として表され、通常は128ビット整数として格納されます

33
Andrew Orsich

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

直接的な回答ではありませんが、_idがObjectIDであるという要件はないことに注意してください。

埋め込みオブジェクトやを含めて、_Iに有効なタイプを設定できます。 GUID for _id)を使用して(一意性の違反がない限り)うまくいくはずです。実際、ObjectIDは実際には単なるカスタムGUIDです。

3

すべてを最初から始める場合は、「Id」メンバーをGuidではなくObjectIdとして入力できます。これは、モデルがMongoDB.Bsonを参照する必要がないため、推奨されます。おそらく、POCOクラスになります。メンバーに「Id」という名前を付ける場合は、[BsonId]属性も必要ありません。前述の理由により、そうしない方がよいでしょう。

POCOクラスですでにObjectIdを使用してスタックしていて、困難を認識している場合、(クラス内の)「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桁の16進文字を使用し、残りの8文字を破棄します。ランダムなObjectId値を保存していて、intを増加するObjectId変換ではない場合は、問題ありません。

ObjectIdGuidとしても書き始めたい場合、base.Deserialize()にフォールバックしている限り、「_ id」タイプを混在させることに何の害もないようです。 、しかし私は間違っている可能性があり、あなたの実装によっては問題になる可能性があります。ドキュメンテーションはどちらかといえば言いません。これを行うには、このオーバーライドを上記のクラスに追加します。

    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());
0
Chad Hedgcock