mirror of https://github.com/abpframework/abp.git
9 changed files with 283 additions and 7 deletions
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using Volo.Abp.Guids; |
|||
using Volo.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Domain.Entities |
|||
{ |
|||
public class DefaultIdGenerator : IIdGenerator, ITransientDependency |
|||
{ |
|||
private readonly IGuidGenerator _guidGenerator; |
|||
|
|||
public DefaultIdGenerator(IGuidGenerator guidGenerator) |
|||
{ |
|||
_guidGenerator = guidGenerator; |
|||
} |
|||
|
|||
public string GenerateStringId() |
|||
{ |
|||
return GenerateGuid().ToString("D"); |
|||
} |
|||
|
|||
public Guid GenerateGuid() |
|||
{ |
|||
return _guidGenerator.Create(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Domain.Entities |
|||
{ |
|||
public interface IIdGenerator |
|||
{ |
|||
string GenerateStringId(); |
|||
|
|||
Guid GenerateGuid(); |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Guids |
|||
{ |
|||
/// <summary>
|
|||
/// Used to generate Ids.
|
|||
/// </summary>
|
|||
public interface IGuidGenerator |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new <see cref="Guid"/>.
|
|||
/// </summary>
|
|||
Guid Create(); |
|||
} |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
using System; |
|||
using System.Security.Cryptography; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.Guids |
|||
{ |
|||
/* This code is taken from jhtodd/SequentialGuid https://github.com/jhtodd/SequentialGuid/blob/master/SequentialGuid/Classes/SequentialGuid.cs */ |
|||
|
|||
/// <summary>
|
|||
/// Implements <see cref="IGuidGenerator"/> by creating sequential Guids.
|
|||
/// Use <see cref="SequentialGuidGeneratorOptions"/> to configure.
|
|||
/// </summary>
|
|||
public class SequentialGuidGenerator : IGuidGenerator |
|||
{ |
|||
public SequentialGuidGeneratorOptions Options { get; } |
|||
|
|||
private static readonly RandomNumberGenerator RandomNumberGenerator = RandomNumberGenerator.Create(); |
|||
|
|||
public SequentialGuidGenerator(IOptions<SequentialGuidGeneratorOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public Guid Create() |
|||
{ |
|||
return Create(Options.DefaultSequentialGuidType); |
|||
} |
|||
|
|||
public Guid Create(SequentialGuidType guidType) |
|||
{ |
|||
// We start with 16 bytes of cryptographically strong random data.
|
|||
var randomBytes = new byte[10]; |
|||
RandomNumberGenerator.Locking(r => r.GetBytes(randomBytes)); |
|||
|
|||
// An alternate method: use a normally-created GUID to get our initial
|
|||
// random data:
|
|||
// byte[] randomBytes = Guid.NewGuid().ToByteArray();
|
|||
// This is faster than using RNGCryptoServiceProvider, but I don't
|
|||
// recommend it because the .NET Framework makes no guarantee of the
|
|||
// randomness of GUID data, and future versions (or different
|
|||
// implementations like Mono) might use a different method.
|
|||
|
|||
// Now we have the random basis for our GUID. Next, we need to
|
|||
// create the six-byte block which will be our timestamp.
|
|||
|
|||
// We start with the number of milliseconds that have elapsed since
|
|||
// DateTime.MinValue. This will form the timestamp. There's no use
|
|||
// being more specific than milliseconds, since DateTime.Now has
|
|||
// limited resolution.
|
|||
|
|||
// Using millisecond resolution for our 48-bit timestamp gives us
|
|||
// about 5900 years before the timestamp overflows and cycles.
|
|||
// Hopefully this should be sufficient for most purposes. :)
|
|||
long timestamp = DateTime.UtcNow.Ticks / 10000L; |
|||
|
|||
// Then get the bytes
|
|||
byte[] timestampBytes = BitConverter.GetBytes(timestamp); |
|||
|
|||
// Since we're converting from an Int64, we have to reverse on
|
|||
// little-endian systems.
|
|||
if (BitConverter.IsLittleEndian) |
|||
{ |
|||
Array.Reverse(timestampBytes); |
|||
} |
|||
|
|||
byte[] guidBytes = new byte[16]; |
|||
|
|||
switch (guidType) |
|||
{ |
|||
case SequentialGuidType.SequentialAsString: |
|||
case SequentialGuidType.SequentialAsBinary: |
|||
|
|||
// For string and byte-array version, we copy the timestamp first, followed
|
|||
// by the random data.
|
|||
Buffer.BlockCopy(timestampBytes, 2, guidBytes, 0, 6); |
|||
Buffer.BlockCopy(randomBytes, 0, guidBytes, 6, 10); |
|||
|
|||
// If formatting as a string, we have to compensate for the fact
|
|||
// that .NET regards the Data1 and Data2 block as an Int32 and an Int16,
|
|||
// respectively. That means that it switches the order on little-endian
|
|||
// systems. So again, we have to reverse.
|
|||
if (guidType == SequentialGuidType.SequentialAsString && BitConverter.IsLittleEndian) |
|||
{ |
|||
Array.Reverse(guidBytes, 0, 4); |
|||
Array.Reverse(guidBytes, 4, 2); |
|||
} |
|||
|
|||
break; |
|||
|
|||
case SequentialGuidType.SequentialAtEnd: |
|||
|
|||
// For sequential-at-the-end versions, we copy the random data first,
|
|||
// followed by the timestamp.
|
|||
Buffer.BlockCopy(randomBytes, 0, guidBytes, 0, 10); |
|||
Buffer.BlockCopy(timestampBytes, 2, guidBytes, 10, 6); |
|||
break; |
|||
} |
|||
|
|||
return new Guid(guidBytes); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
namespace Volo.Abp.Guids |
|||
{ |
|||
public class SequentialGuidGeneratorOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Default value: <see cref="SequentialGuidType.SequentialAtEnd"/>.
|
|||
/// </summary>
|
|||
public SequentialGuidType DefaultSequentialGuidType { get; set; } |
|||
|
|||
public SequentialGuidGeneratorOptions() |
|||
{ |
|||
DefaultSequentialGuidType = SequentialGuidType.SequentialAtEnd; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Guids |
|||
{ |
|||
/// <summary>
|
|||
/// Describes the type of a sequential GUID value.
|
|||
/// </summary>
|
|||
public enum SequentialGuidType |
|||
{ |
|||
/// <summary>
|
|||
/// The GUID should be sequential when formatted using the <see cref="Guid.ToString()" /> method.
|
|||
/// Used by MySql and PostgreSql.
|
|||
/// </summary>
|
|||
SequentialAsString, |
|||
|
|||
/// <summary>
|
|||
/// The GUID should be sequential when formatted using the <see cref="Guid.ToByteArray" /> method.
|
|||
/// Used by Oracle.
|
|||
/// </summary>
|
|||
SequentialAsBinary, |
|||
|
|||
/// <summary>
|
|||
/// The sequential portion of the GUID should be located at the end of the Data4 block.
|
|||
/// Used by SqlServer.
|
|||
/// </summary>
|
|||
SequentialAtEnd |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Guids |
|||
{ |
|||
/// <summary>
|
|||
/// Implements <see cref="IGuidGenerator"/> by using <see cref="Guid.NewGuid"/>.
|
|||
/// </summary>
|
|||
public class SimpleGuidGenerator : IGuidGenerator |
|||
{ |
|||
public virtual Guid Create() |
|||
{ |
|||
return Guid.NewGuid(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Threading |
|||
{ |
|||
/// <summary>
|
|||
/// Extension methods to make locking easier.
|
|||
/// </summary>
|
|||
public static class LockExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Executes given <paramref name="action"/> by locking given <paramref name="source"/> object.
|
|||
/// </summary>
|
|||
/// <param name="source">Source object (to be locked)</param>
|
|||
/// <param name="action">Action (to be executed)</param>
|
|||
public static void Locking(this object source, Action action) |
|||
{ |
|||
lock (source) |
|||
{ |
|||
action(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes given <paramref name="action"/> by locking given <paramref name="source"/> object.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Type of the object (to be locked)</typeparam>
|
|||
/// <param name="source">Source object (to be locked)</param>
|
|||
/// <param name="action">Action (to be executed)</param>
|
|||
public static void Locking<T>(this T source, Action<T> action) where T : class |
|||
{ |
|||
lock (source) |
|||
{ |
|||
action(source); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes given <paramref name="func"/> and returns it's value by locking given <paramref name="source"/> object.
|
|||
/// </summary>
|
|||
/// <typeparam name="TResult">Return type</typeparam>
|
|||
/// <param name="source">Source object (to be locked)</param>
|
|||
/// <param name="func">Function (to be executed)</param>
|
|||
/// <returns>Return value of the <paramref name="func"/></returns>
|
|||
public static TResult Locking<TResult>(this object source, Func<TResult> func) |
|||
{ |
|||
lock (source) |
|||
{ |
|||
return func(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Executes given <paramref name="func"/> and returns it's value by locking given <paramref name="source"/> object.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">Type of the object (to be locked)</typeparam>
|
|||
/// <typeparam name="TResult">Return type</typeparam>
|
|||
/// <param name="source">Source object (to be locked)</param>
|
|||
/// <param name="func">Function (to be executed)</param>
|
|||
/// <returns>Return value of the <paramnref name="func"/></returns>
|
|||
public static TResult Locking<T, TResult>(this T source, Func<T, TResult> func) where T : class |
|||
{ |
|||
lock (source) |
|||
{ |
|||
return func(source); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue