Browse Source

Added Guid generator and Id generators.

pull/81/head
Halil İbrahim Kalkan 9 years ago
parent
commit
fbbc63536b
  1. 9
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
  2. 26
      src/Volo.Abp/Volo/Abp/Domain/Entities/DefaultIdGenerator.cs
  3. 11
      src/Volo.Abp/Volo/Abp/Domain/Entities/IdGenerator.cs
  4. 15
      src/Volo.Abp/Volo/Abp/Guids/IGuidGenerator.cs
  5. 103
      src/Volo.Abp/Volo/Abp/Guids/SequentialGuidGenerator.cs
  6. 15
      src/Volo.Abp/Volo/Abp/Guids/SequentialGuidGeneratorOptions.cs
  7. 28
      src/Volo.Abp/Volo/Abp/Guids/SequentialGuidType.cs
  8. 15
      src/Volo.Abp/Volo/Abp/Guids/SimpleGuidGenerator.cs
  9. 68
      src/Volo.Abp/Volo/Abp/Threading/LockExtensions.cs

9
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs

@ -89,19 +89,14 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
public override TEntity Update(TEntity entity) public override TEntity Update(TEntity entity)
{ {
//TODO: This code is got from UserStore.UpdateAsync and revised Update method based on that, but we should be sure that it's valid DbContext.Attach(entity);
//Context.Attach(user);
//user.ConcurrencyStamp = Guid.NewGuid().ToString();
//Context.Update(user);
DbContext.Attach(entity); //TODO: What is different for DbSet.Attach(entity)?
if (entity is IHasConcurrencyStamp) if (entity is IHasConcurrencyStamp)
{ {
(entity as IHasConcurrencyStamp).ConcurrencyStamp = Guid.NewGuid().ToString(); //TODO: Use IGuidGenerator! (entity as IHasConcurrencyStamp).ConcurrencyStamp = Guid.NewGuid().ToString(); //TODO: Use IGuidGenerator!
} }
return DbContext.Update(entity).Entity; //TODO: or DbSet.Update(entity) ? return DbContext.Update(entity).Entity;
} }
public override void Delete(TEntity entity) public override void Delete(TEntity entity)

26
src/Volo.Abp/Volo/Abp/Domain/Entities/DefaultIdGenerator.cs

@ -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();
}
}
}

11
src/Volo.Abp/Volo/Abp/Domain/Entities/IdGenerator.cs

@ -0,0 +1,11 @@
using System;
namespace Volo.Abp.Domain.Entities
{
public interface IIdGenerator
{
string GenerateStringId();
Guid GenerateGuid();
}
}

15
src/Volo.Abp/Volo/Abp/Guids/IGuidGenerator.cs

@ -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();
}
}

103
src/Volo.Abp/Volo/Abp/Guids/SequentialGuidGenerator.cs

@ -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);
}
}
}

15
src/Volo.Abp/Volo/Abp/Guids/SequentialGuidGeneratorOptions.cs

@ -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;
}
}
}

28
src/Volo.Abp/Volo/Abp/Guids/SequentialGuidType.cs

@ -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
}
}

15
src/Volo.Abp/Volo/Abp/Guids/SimpleGuidGenerator.cs

@ -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();
}
}
}

68
src/Volo.Abp/Volo/Abp/Threading/LockExtensions.cs

@ -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…
Cancel
Save