• 作者:老汪软件技巧
  • 发表时间:2024-12-07 17:03
  • 浏览量:

前言

因为很多场景下我们需要在创建MongoDB数据的时候提前生成好主键,像在EF中我们可以通过Guid.NewGuid()来生成主键,本来想着要不要实现一套MongoDB中ObjectId的,结果发现网上各种各样的实现都有,不过好在阅读C#MongoDB驱动mongo-csharp-driver代码的时候发现有ObjectId.GenerateNewId()的方法提供,我们可以直接调用即可,不需要我们在花费多余的时间设计重写了。

MongoDB ObjectId类型概述

每次插入一条数据系统都会自动插入一个_id键,键值不可以重复,它可以是任何类型的,也可以手动的插入,默认情况下它的数据类型是ObjectId,由于MongoDB在设计之初就是用作分布式数据库,所以使用ObjectId可以避免不同数据库中_id的重复(如果使用自增的方式在分布式系统中就会出现重复的_id的值)。 ObjectId使用12字节的存储空间,每个字节可以存储两个十六进制数字,所以一共可以存储24个十六进制数字组成的字符串,在这24个字符串中,前8位表示时间戳,接下来6位是一个机器码,接下来4位表示进程id,最后6位表示计数器。

MongoDB 采用 ObjectId 来表示主键的类型,数据库中每个文档都拥有一个_id 字段表示主键,_id 的生成规则如下:

其中包括:4-byte Unix 时间戳,3-byte 机器 ID,2-byte 进程 ID,3-byte 计数器(初始化随机)。

601e2b6b  a3203c  c89f   2d31aa
   ↑        ↑       ↑       ↑
 时间戳    机器码   进程ID   随机数

MongoDB.Driver驱动安装1、直接命令自动安装

Install-Package MongoDB.Driver

2、搜索Nuget手动安装

调用生成主键ObjectId

var primarykeyId = ObjectId.GenerateNewId();
//输出:641c54b2e674000035001dc2

mongo-csharp-driver ObjectId源码详解

关于ObjectId的生成原理大家阅读如下源码即可。

/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Security;
using System.Threading;
namespace MongoDB.Bson
{
    /// 
    /// Represents an ObjectId (see also BsonObjectId).
    /// 
#if NET45
    [Serializable]
#endif
    public struct ObjectId : IComparable, IEquatable, IConvertible
    {
        // private static fields
        private static readonly ObjectId __emptyInstance = default(ObjectId);
        private static readonly int __staticMachine = (GetMachineHash() + GetAppDomainId()) & 0x00ffffff;
        private static readonly short __staticPid = GetPid();
        private static int __staticIncrement = (new Random()).Next();
        // private fields
        private readonly int _a;
        private readonly int _b;
        private readonly int _c;
        // constructors
        /// 
        /// Initializes a new instance of the ObjectId class.
        /// 
        /// The bytes.
        public ObjectId(byte[] bytes)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }
            if (bytes.Length != 12)
            {
                throw new ArgumentException("Byte array must be 12 bytes long""bytes");
            }
            FromByteArray(bytes, 0out _a, out _b, out _c);
        }
        /// 
        /// Initializes a new instance of the ObjectId class.
        /// 
        /// The bytes.
        /// The index into the byte array where the ObjectId starts.
        internal ObjectId(byte[] bytes, int index)
        {
            FromByteArray(bytes, index, out _a, out _b, out _c);
        }
        /// 
        /// Initializes a new instance of the ObjectId class.
        /// 
        /// The timestamp (expressed as a DateTime).
        /// The machine hash.
        /// The PID.
        /// The increment.
        public ObjectId(DateTime timestamp, int machine, short pid, int increment)
            : this(GetTimestampFromDateTime(timestamp), machine, pid, increment)
        {
        }
        /// 
        /// Initializes a new instance of the ObjectId class.
        /// 
        /// The timestamp.
        /// The machine hash.
        /// The PID.
        /// The increment.
        public ObjectId(int timestamp, int machine, short pid, int increment)
        {
            if ((machine & 0xff000000) != 0)
            {
                throw new ArgumentOutOfRangeException("machine""The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
            }
            if ((increment & 0xff000000) != 0)
            {
                throw new ArgumentOutOfRangeException("increment""The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
            }
            _a = timestamp;
            _b = (machine << 8) | (((int)pid >> 8) & 0xff);
            _c = ((int)pid << 24) | increment;
        }
        /// 
        /// Initializes a new instance of the ObjectId class.
        /// 
        /// The value.
        public ObjectId(string value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            var bytes = BsonUtils.ParseHexString(value);
            FromByteArray(bytes, 0out _a, out _b, out _c);
        }
        // public static properties
        /// 
        /// Gets an instance of ObjectId where the value is empty.
        /// 
        public static ObjectId Empty
        {
            get { return __emptyInstance; }
        }
        // public properties
        /// 
        /// Gets the timestamp.
        /// 
        public int Timestamp
        {
            get { return _a; }
        }
        /// 
        /// Gets the machine.
        /// 
        public int Machine
        {
            get { return (_b >> 8) & 0xffffff; }
        }
        /// 
        /// Gets the PID.
        /// 
        public short Pid
        {
            get { return (short)(((_b << 8) & 0xff00) | ((_c >> 24) & 0x00ff)); }
        }
        /// 
        /// Gets the increment.
        /// 
        public int Increment
        {
            get { return _c & 0xffffff; }
        }
        /// 
        /// Gets the creation time (derived from the timestamp).
        /// 
        public DateTime CreationTime
        {
            get { return BsonConstants.UnixEpoch.AddSeconds(Timestamp); }
        }
        // public operators
        /// 
        /// Compares two ObjectIds.
        /// 
        /// The first ObjectId.
        /// The other ObjectId
        /// True if the first ObjectId is less than the second ObjectId.
        public static bool operator <(ObjectId lhs, ObjectId rhs)
        {
            return lhs.CompareTo(rhs) < 0;
        }
        /// 
        /// Compares two ObjectIds.
        /// 
        /// The first ObjectId.
        /// The other ObjectId
        /// True if the first ObjectId is less than or equal to the second ObjectId.
        public static bool operator <=(ObjectId lhs, ObjectId rhs)
        {
            return lhs.CompareTo(rhs) <= 0;
        }
        /// 
        /// Compares two ObjectIds.
        /// 
        /// The first ObjectId.
        /// The other ObjectId.
        /// True if the two ObjectIds are equal.
        public static bool operator ==(ObjectId lhs, ObjectId rhs)
        {
            return lhs.Equals(rhs);
        }
        /// 
        /// Compares two ObjectIds.
        /// 
        /// The first ObjectId.
        /// The other ObjectId.
        /// True if the two ObjectIds are not equal.
        public static bool operator !=(ObjectId lhs, ObjectId rhs)
        {
            return !(lhs == rhs);
        }
        /// 
        /// Compares two ObjectIds.
        /// 
        /// The first ObjectId.
        /// The other ObjectId
        /// True if the first ObjectId is greather than or equal to the second ObjectId.
        public static bool operator >=(ObjectId lhs, ObjectId rhs)
        {
            return lhs.CompareTo(rhs) >= 0;
        }
        /// 
        /// Compares two ObjectIds.
        /// 
        /// The first ObjectId.
        /// The other ObjectId
        /// True if the first ObjectId is greather than the second ObjectId.
        public static bool operator >(ObjectId lhs, ObjectId rhs)
        {
            return lhs.CompareTo(rhs) > 0;
        }
        // public static methods
        /// 
        /// Generates a new ObjectId with a unique value.
        /// 
        /// An ObjectId.
        public static ObjectId GenerateNewId()
        {
            return GenerateNewId(GetTimestampFromDateTime(DateTime.UtcNow));
        }
        /// 
        /// Generates a new ObjectId with a unique value (with the timestamp component based on a given DateTime).
        /// 
        /// The timestamp component (expressed as a DateTime).
        /// An ObjectId.
        public static ObjectId GenerateNewId(DateTime timestamp)
        {
            return GenerateNewId(GetTimestampFromDateTime(timestamp));
        }
        /// 
        /// Generates a new ObjectId with a unique value (with the given timestamp).
        /// 
        /// The timestamp component.
        /// An ObjectId.
        public static ObjectId GenerateNewId(int timestamp)
        {
            int increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff// only use low order 3 bytes
            return new ObjectId(timestamp, __staticMachine, __staticPid, increment);
        }
        /// 
        /// Packs the components of an ObjectId into a byte array.
        /// 
        /// The timestamp.
        /// The machine hash.
        /// The PID.
        /// The increment.
        /// A byte array.
        public static byte[] Pack(int timestamp, int machine, short pid, int increment)
        {
            if ((machine & 0xff000000) != 0)
            {
                throw new ArgumentOutOfRangeException("machine""The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
            }
            if ((increment & 0xff000000) != 0)
            {
                throw new ArgumentOutOfRangeException("increment""The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
            }
            byte[] bytes = new byte[12];
            bytes[0] = (byte)(timestamp >> 24);
            bytes[1] = (byte)(timestamp >> 16);
            bytes[2] = (byte)(timestamp >> 8);
            bytes[3] = (byte)(timestamp);
            bytes[4] = (byte)(machine >> 16);
            bytes[5] = (byte)(machine >> 8);
            bytes[6] = (byte)(machine);
            bytes[7] = (byte)(pid >> 8);
            bytes[8] = (byte)(pid);
            bytes[9] = (byte)(increment >> 16);
            bytes[10] = (byte)(increment >> 8);
            bytes[11] = (byte)(increment);
            return bytes;
        }
        /// 
        /// Parses a string and creates a new ObjectId.
        /// 
        /// The string value.
        /// A ObjectId.
        public static ObjectId Parse(string s)
        {
            if (s == null)
            {
                throw new ArgumentNullException("s");
            }
            ObjectId objectId;
            if (TryParse(s, out objectId))
            {
                return objectId;
            }
            else
            {
                var message = string.Format("'{0}' is not a valid 24 digit hex string.", s);
                throw new FormatException(message);
            }
        }
        /// 
        /// Tries to parse a string and create a new ObjectId.
        /// 

主键生成策略java代码_主键自动生成_

        /// The string value.         /// The new ObjectId.         /// True if the string was parsed successfully.         public static bool TryParse(string s, out ObjectId objectId)         {             // don't throw ArgumentNullException if s is null             if (s != null && s.Length == 24)             {                 byte[] bytes;                 if (BsonUtils.TryParseHexString(s, out bytes))                 {                     objectId = new ObjectId(bytes);                     return true;                 }             }             objectId = default(ObjectId);             return false;         }         ///          /// Unpacks a byte array into the components of an ObjectId.         ///          /// A byte array.         /// The timestamp.         /// The machine hash.         /// The PID.         /// The increment.         public static void Unpack(byte[] bytes, out int timestamp, out int machine, out short pid, out int increment)         {             if (bytes == null)             {                 throw new ArgumentNullException("bytes");             }             if (bytes.Length != 12)             {                 throw new ArgumentOutOfRangeException("bytes""Byte array must be 12 bytes long.");             }             timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];             machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6];             pid = (short)((bytes[7] << 8) + bytes[8]);             increment = (bytes[9] << 16) + (bytes[10] << 8) + bytes[11];         }         // private static methods         private static int GetAppDomainId()         { #if NETSTANDARD1_5 || NETSTANDARD1_6             return 1; #else             return AppDomain.CurrentDomain.Id; #endif         }         ///          /// Gets the current process id.  This method exists because of how CAS operates on the call stack, checking         /// for permissions before executing the method.  Hence, if we inlined this call, the calling method would not execute         /// before throwing an exception requiring the try/catch at an even higher level that we don't necessarily control.         ///          [MethodImpl(MethodImplOptions.NoInlining)]         private static int GetCurrentProcessId()         {             return Process.GetCurrentProcess().Id;         }         private static int GetMachineHash()         {             // use instead of Dns.HostName so it will work offline             var machineName = GetMachineName();             return 0x00ffffff & machineName.GetHashCode(); // use first 3 bytes of hash         }         private static string GetMachineName()         {             return Environment.MachineName;         }         private static short GetPid()         {             try             {                 return (short)GetCurrentProcessId(); // use low order two bytes only             }             catch (SecurityException)             {                 return 0;             }         }         private static int GetTimestampFromDateTime(DateTime timestamp)         {             var secondsSinceEpoch = (long)Math.Floor((BsonUtils.ToUniversalTime(timestamp) - BsonConstants.UnixEpoch).TotalSeconds);             if (secondsSinceEpoch < int.MinValue || secondsSinceEpoch > int.MaxValue)             {                 throw new ArgumentOutOfRangeException("timestamp");             }             return (int)secondsSinceEpoch;         }         private static void FromByteArray(byte[] bytes, int offset, out int a, out int b, out int c)         {             a = (bytes[offset] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3];             b = (bytes[offset + 4] << 24) | (bytes[offset + 5] << 16) | (bytes[offset + 6] << 8) | bytes[offset + 7];             c = (bytes[offset + 8] << 24) | (bytes[offset + 9] << 16) | (bytes[offset + 10] << 8) | bytes[offset + 11];         }         // public methods         ///          /// Compares this ObjectId to another ObjectId.         ///          /// The other ObjectId.         /// A 32-bit signed integer that indicates whether this ObjectId is less than, equal to, or greather than the other.         public int CompareTo(ObjectId other)         {             int result = ((uint)_a).CompareTo((uint)other._a);             if (result != 0) { return result; }             result = ((uint)_b).CompareTo((uint)other._b);             if (result != 0) { return result; }             return ((uint)_c).CompareTo((uint)other._c);         }         ///          /// Compares this ObjectId to another ObjectId.         ///          /// The other ObjectId.         /// True if the two ObjectIds are equal.         public bool Equals(ObjectId rhs)         {             return                 _a == rhs._a &&                 _b == rhs._b &&                 _c == rhs._c;         }         ///          /// Compares this ObjectId to another object.         ///          /// The other object.         /// True if the other object is an ObjectId and equal to this one.         public override bool Equals(object obj)         {             if (obj is ObjectId)             {                 return Equals((ObjectId)obj);             }             else             {                 return false;             }         }         ///          /// Gets the hash code.         ///          /// The hash code.         public override int GetHashCode()         {             int hash = 17;             hash = 37 * hash + _a.GetHashCode();             hash = 37 * hash + _b.GetHashCode();             hash = 37 * hash + _c.GetHashCode();             return hash;         }         ///          /// Converts the ObjectId to a byte array.         ///          /// A byte array.         public byte[] ToByteArray()         {             var bytes = new byte[12];             ToByteArray(bytes, 0);             return bytes;         }         ///          /// Converts the ObjectId to a byte array.         ///          /// The destination.         /// The offset.         public void ToByteArray(byte[] destination, int offset)         {             if (destination == null)             {                 throw new ArgumentNullException("destination");             }             if (offset + 12 > destination.Length)             {                 throw new ArgumentException("Not enough room in destination buffer.""offset");             }             destination[offset + 0] = (byte)(_a >> 24);             destination[offset + 1] = (byte)(_a >> 16);             destination[offset + 2] = (byte)(_a >> 8);             destination[offset + 3] = (byte)(_a);             destination[offset + 4] = (byte)(_b >> 24);             destination[offset + 5] = (byte)(_b >> 16);             destination[offset + 6] = (byte)(_b >> 8);             destination[offset + 7] = (byte)(_b);             destination[offset + 8] = (byte)(_c >> 24);             destination[offset + 9] = (byte)(_c >> 16);             destination[offset + 10] = (byte)(_c >> 8);             destination[offset + 11] = (byte)(_c);         }         ///          /// Returns a string representation of the value.         ///          /// A string representation of the value.         public override string ToString()         {             var c = new char[24];             c[0] = BsonUtils.ToHexChar((_a >> 28) & 0x0f);             c[1] = BsonUtils.ToHexChar((_a >> 24) & 0x0f);             c[2] = BsonUtils.ToHexChar((_a >> 20) & 0x0f);             c[3] = BsonUtils.ToHexChar((_a >> 16) & 0x0f);             c[4] = BsonUtils.ToHexChar((_a >> 12) & 0x0f);             c[5] = BsonUtils.ToHexChar((_a >> 8) & 0x0f);             c[6] = BsonUtils.ToHexChar((_a >> 4) & 0x0f);             c[7] = BsonUtils.ToHexChar(_a & 0x0f);             c[8] = BsonUtils.ToHexChar((_b >> 28) & 0x0f);             c[9] = BsonUtils.ToHexChar((_b >> 24) & 0x0f);             c[10] = BsonUtils.ToHexChar((_b >> 20) & 0x0f);             c[11] = BsonUtils.ToHexChar((_b >> 16) & 0x0f);             c[12] = BsonUtils.ToHexChar((_b >> 12) & 0x0f);             c[13] = BsonUtils.ToHexChar((_b >> 8) & 0x0f);             c[14] = BsonUtils.ToHexChar((_b >> 4) & 0x0f);             c[15] = BsonUtils.ToHexChar(_b & 0x0f);             c[16] = BsonUtils.ToHexChar((_c >> 28) & 0x0f);             c[17] = BsonUtils.ToHexChar((_c >> 24) & 0x0f);             c[18] = BsonUtils.ToHexChar((_c >> 20) & 0x0f);             c[19] = BsonUtils.ToHexChar((_c >> 16) & 0x0f);             c[20] = BsonUtils.ToHexChar((_c >> 12) & 0x0f);             c[21] = BsonUtils.ToHexChar((_c >> 8) & 0x0f);             c[22] = BsonUtils.ToHexChar((_c >> 4) & 0x0f);             c[23] = BsonUtils.ToHexChar(_c & 0x0f);             return new string(c);         }         // explicit IConvertible implementation         TypeCode IConvertible.GetTypeCode()         {             return TypeCode.Object;         }         bool IConvertible.ToBoolean(IFormatProvider provider)         {             throw new InvalidCastException();         }         byte IConvertible.ToByte(IFormatProvider provider)         {             throw new InvalidCastException();         }         char IConvertible.ToChar(IFormatProvider provider)         {             throw new InvalidCastException();         }         DateTime IConvertible.ToDateTime(IFormatProvider provider)         {             throw new InvalidCastException();         }         decimal IConvertible.ToDecimal(IFormatProvider provider)         {             throw new InvalidCastException();         }         double IConvertible.ToDouble(IFormatProvider provider)         {             throw new InvalidCastException();         }         short IConvertible.ToInt16(IFormatProvider provider)         {             throw new InvalidCastException();         }         int IConvertible.ToInt32(IFormatProvider provider)         {             throw new InvalidCastException();         }         long IConvertible.ToInt64(IFormatProvider provider)         {             throw new InvalidCastException();         }         sbyte IConvertible.ToSByte(IFormatProvider provider)         {             throw new InvalidCastException();         }         float IConvertible.ToSingle(IFormatProvider provider)         {             throw new InvalidCastException();         }         string IConvertible.ToString(IFormatProvider provider)         {             return ToString();         }         object IConvertible.ToType(Type conversionType, IFormatProvider provider)         {             switch (Type.GetTypeCode(conversionType))             {                 case TypeCode.String:                     return ((IConvertible)this).ToString(provider);                 case TypeCode.Object:                     if (conversionType == typeof(object) || conversionType == typeof(ObjectId))                     {                         return this;                     }                     if (conversionType == typeof(BsonObjectId))                     {                         return new BsonObjectId(this);                     }                     if (conversionType == typeof(BsonString))                     {                         return new BsonString(((IConvertible)this).ToString(provider));                     }                     break;             }             throw new InvalidCastException();         }         ushort IConvertible.ToUInt16(IFormatProvider provider)         {             throw new InvalidCastException();         }         uint IConvertible.ToUInt32(IFormatProvider provider)         {             throw new InvalidCastException();         }         ulong IConvertible.ToUInt64(IFormatProvider provider)         {             throw new InvalidCastException();         }     } }