Permalink
Browse files

CSHARP-700: Improvements to CombGuidGenerator and its unit tests.

  • Loading branch information...
1 parent 2dede50 commit 05b35a03454e873f87bece6a7ce5c0aa6e199b7e rstam committed Apr 4, 2013
@@ -51,34 +51,51 @@ public static CombGuidGenerator Instance
/// <returns>An Id.</returns>
public object GenerateId(object container, object document)
{
+ var guid = Guid.NewGuid();
+ var timestamp = DateTime.UtcNow;
+ return NewCombGuid(guid, timestamp);
+ }
+
+ /// <summary>
+ /// Tests whether an Id is empty.
+ /// </summary>
+ /// <param name="id">The Id.</param>
+ /// <returns>True if the Id is empty.</returns>
+ public bool IsEmpty(object id)
+ {
+ return id == null || (Guid)id == Guid.Empty;
+ }
+
+ /// <summary>
+ /// Create a new CombGuid from a given Guid and timestamp.
+ /// </summary>
+ /// <param name="guid">The base Guid.</param>
+ /// <param name="timestamp">The timestamp.</param>
+ /// <returns>A new CombGuid created by combining the base Guid with the timestamp.</returns>
+ public Guid NewCombGuid(Guid guid, DateTime timestamp)
+ {
+ // note: Guids generated by CombGuidGenerator are only considered ascending by SQL Server which compares Guids in an unusual way
+ // to generate Guids considered ascending by MongoDB use the AscendingGuidGenerator
+
var baseDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);
- var now = DateTime.UtcNow;
- var days = (ushort)(now - baseDate).TotalDays;
- var milliseconds = (int)now.TimeOfDay.TotalMilliseconds;
+ var days = (ushort)(timestamp - baseDate).Days;
+ var timeTicks = (int)(timestamp.TimeOfDay.Ticks * 300 / TimeSpan.TicksPerSecond); // convert from .NET resolution to SQL Server resolution
- // replace last 6 bytes of a new Guid with 2 bytes from days and 4 bytes from milliseconds
+ // replace last 6 bytes of a new Guid with 2 bytes from days and 4 bytes from time of day
// see: The Cost of GUIDs as Primary Keys by Jimmy Nilson
// at: http://www.informit.com/articles/article.aspx?p=25862&seqNum=7
- var bytes = Guid.NewGuid().ToByteArray();
+ var bytes = guid.ToByteArray();
+
Array.Copy(BitConverter.GetBytes(days), 0, bytes, 10, 2);
- Array.Copy(BitConverter.GetBytes(milliseconds), 0, bytes, 12, 4);
+ Array.Copy(BitConverter.GetBytes(timeTicks), 0, bytes, 12, 4);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(bytes, 10, 2);
Array.Reverse(bytes, 12, 4);
}
- return new Guid(bytes);
- }
- /// <summary>
- /// Tests whether an Id is empty.
- /// </summary>
- /// <param name="id">The Id.</param>
- /// <returns>True if the Id is empty.</returns>
- public bool IsEmpty(object id)
- {
- return id == null || (Guid)id == Guid.Empty;
+ return new Guid(bytes);
}
}
}
@@ -118,6 +118,7 @@
<Compile Include="Serialization\Conventions\ReadWriteMemberFinderConventionsTests.cs" />
<Compile Include="Serialization\Conventions\StringObjectIdGeneratorConventionsTests.cs" />
<Compile Include="Serialization\IdGenerators\AscendingGuidGeneratorTests.cs" />
+ <Compile Include="Serialization\IdGenerators\CombGuidGeneratorTests.cs" />
<Compile Include="Serialization\LegacyBsonClassMapTests.cs" />
<Compile Include="Serialization\Serializers\ExtraElementsTests.cs" />
<Compile Include="Serialization\Options\RepresentationSerializationOptionsTests.cs" />
@@ -0,0 +1,61 @@
+/* Copyright 2010-2013 10gen 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.Linq;
+using MongoDB.Bson.Serialization;
+using MongoDB.Bson.Serialization.IdGenerators;
+using NUnit.Framework;
+
+namespace MongoDB.BsonUnitTests.Serialization
+{
+ [TestFixture]
+ public class CombGuidGeneratorTests
+ {
+ private CombGuidGenerator _generator = new CombGuidGenerator();
+
+ [Test]
+ public void TestNewCombGuid()
+ {
+ var guid = Guid.NewGuid();
+ var timestamp = new DateTime(2013, 4, 2, 0, 0, 0, 500, DateTimeKind.Utc); // half a second past midnight
+ var combGuid = _generator.NewCombGuid(guid, timestamp);
+
+ var expectedDays = (short)(timestamp.Date - new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).Days;
+ var expectedTimeTicks = 150; // half a second in SQL Server resolution
+
+ var bytes = combGuid.ToByteArray();
+ if (BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(bytes, 10, 2);
+ Array.Reverse(bytes, 12, 4);
+ }
+ var days = BitConverter.ToInt16(bytes, 10);
+ var timeTicks = BitConverter.ToInt32(bytes, 12);
+
+ Assert.IsTrue(guid.ToByteArray().Take(10).SequenceEqual(bytes.Take(10))); // first 10 bytes are from the base Guid
+ Assert.AreEqual(expectedDays, days);
+ Assert.AreEqual(expectedTimeTicks, timeTicks);
+ }
+
+ [Test]
+ public void TestIsEmpty()
+ {
+ Assert.IsTrue(_generator.IsEmpty(null));
+ Assert.IsTrue(_generator.IsEmpty(Guid.Empty));
+ Assert.IsFalse(_generator.IsEmpty(Guid.NewGuid()));
+ }
+ }
+}

0 comments on commit 05b35a0

Please sign in to comment.