C# 生成 MongoDB 中的 ObjectId
ObjectId介绍在MongoDB中,文档(document)在集合(collection)中的存储需要一个唯一的_id字段作为主键。这个_id默认使用ObjectId来定义,因为ObjectId定义的足够短小,并尽最大可能的保持唯一性,同时能被快速的生成。
ObjectId 是一个 12 Bytes 的 BSON 类型,其包含:
[*]4 Bytes 自纪元时间开始的秒数
[*]3 Bytes 机器描述符
[*]2 Bytes 进程ID
[*]3 Bytes 随机数
从定义可以看出,在同一秒内,在不同的机器上相同进程ID条件下,非常有可能生成相同的ObjectId。
同时可以根据定义判断出,在给定条件下,ObjectId本身即可描述生成的时间顺序
ObjectId的存储使用Byte数组,而其展现需将Byte数组转换成字符串进行显示,所以通常我们看到的ObjectId都类似于:
ObjectId("507f191e810c19729de860ea")
C#定义ObjectId类
View Code
1 public class ObjectId
2 {
3 private string _string;
4
5 public ObjectId()
6 {
7 }
8
9 public ObjectId(string value)
10 : this(DecodeHex(value))
11 {
12 }
13
14 internal ObjectId(byte[] value)
15 {
16 Value = value;
17 }
18
19 public static ObjectId Empty
20 {
21 get { return new ObjectId("000000000000000000000000"); }
22 }
23
24 public byte[] Value { get; private set; }
25
26 public static ObjectId NewObjectId()
27 {
28 return new ObjectId { Value = ObjectIdGenerator.Generate() };
29 }
30
31 public static bool TryParse(string value, out ObjectId objectId)
32 {
33 objectId = Empty;
34 if (value == null || value.Length != 24)
35 {
36 return false;
37 }
38
39 try
40 {
41 objectId = new ObjectId(value);
42 return true;
43 }
44 catch (FormatException)
45 {
46 return false;
47 }
48 }
49
50 protected static byte[] DecodeHex(string value)
51 {
52 if (string.IsNullOrEmpty(value))
53 throw new ArgumentNullException("value");
54
55 var chars = value.ToCharArray();
56 var numberChars = chars.Length;
57 var bytes = new byte;
58
59 for (var i = 0; i < numberChars; i += 2)
60 {
61 bytes = Convert.ToByte(new string(chars, i, 2), 16);
62 }
63
64 return bytes;
65 }
66
67 public override int GetHashCode()
68 {
69 return Value != null ? ToString().GetHashCode() : 0;
70 }
71
72 public override string ToString()
73 {
74 if (_string == null && Value != null)
75 {
76 _string = BitConverter.ToString(Value)
77 .Replace("-", string.Empty)
78 .ToLowerInvariant();
79 }
80
81 return _string;
82 }
83
84 public override bool Equals(object obj)
85 {
86 var other = obj as ObjectId;
87 return Equals(other);
88 }
89
90 public bool Equals(ObjectId other)
91 {
92 return other != null && ToString() == other.ToString();
93 }
94
95 public static implicit operator string(ObjectId objectId)
96 {
97 return objectId == null ? null : objectId.ToString();
98 }
99
100 public static implicit operator ObjectId(string value)
101 {
102 return new ObjectId(value);
103 }
104
105 public static bool operator ==(ObjectId left, ObjectId right)
106 {
107 if (ReferenceEquals(left, right))
108 {
109 return true;
110 }
111
112 if (((object)left == null) || ((object)right == null))
113 {
114 return false;
115 }
116
117 return left.Equals(right);
118 }
119
120 public static bool operator !=(ObjectId left, ObjectId right)
121 {
122 return !(left == right);
123 }
124 }
C#实现ObjectId的生成器
View Code
1 internal static class ObjectIdGenerator
2 {
3 private static readonly DateTime Epoch =
4 new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
5 private static readonly object _innerLock = new object();
6 private static int _counter;
7 private static readonly byte[] _machineHash = GenerateHostHash();
8 private static readonly byte[] _processId =
9 BitConverter.GetBytes(GenerateProcessId());
10
11 public static byte[] Generate()
12 {
13 var oid = new byte;
14 var copyidx = 0;
15
16 Array.Copy(BitConverter.GetBytes(GenerateTime()), 0, oid, copyidx, 4);
17 copyidx += 4;
18
19 Array.Copy(_machineHash, 0, oid, copyidx, 3);
20 copyidx += 3;
21
22 Array.Copy(_processId, 0, oid, copyidx, 2);
23 copyidx += 2;
24
25 Array.Copy(BitConverter.GetBytes(GenerateCounter()), 0, oid, copyidx, 3);
26
27 return oid;
28 }
29
30 private static int GenerateTime()
31 {
32 var now = DateTime.UtcNow;
33 var nowtime = new DateTime(Epoch.Year, Epoch.Month, Epoch.Day,
34 now.Hour, now.Minute, now.Second, now.Millisecond);
35 var diff = nowtime - Epoch;
36 return Convert.ToInt32(Math.Floor(diff.TotalMilliseconds));
37 }
38
39 private static byte[] GenerateHostHash()
40 {
41 using (var md5 = MD5.Create())
42 {
43 var host = Dns.GetHostName();
44 return md5.ComputeHash(Encoding.Default.GetBytes(host));
45 }
46 }
47
48 private static int GenerateProcessId()
49 {
50 var process = Process.GetCurrentProcess();
51 return process.Id;
52 }
53
54 private static int GenerateCounter()
55 {
56 lock (_innerLock)
57 {
58 return _counter++;
59 }
60 }
61 }
使用举例
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Console.ForegroundColor = ConsoleColor.Red;
6
7 ObjectId emptyOid = ObjectId.Empty;
8 Console.WriteLine(emptyOid);
9
10 Console.WriteLine();
11 Console.ForegroundColor = ConsoleColor.Green;
12
13 for (int i = 0; i < 10; i++)
14 {
15 ObjectId oid = ObjectId.NewObjectId();
16 Console.WriteLine(oid);
17 }
18
19 Console.WriteLine();
20 Console.ForegroundColor = ConsoleColor.Blue;
21
22 ObjectId existingOid;
23 ObjectId.TryParse("507f191e810c19729de860ea", out existingOid);
24 Console.WriteLine(existingOid);
25
26 Console.ReadKey();
27 }
28 }
页:
[1]