diff --git a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
index 5f57032272..8cb88d38f0 100644
--- a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
+++ b/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)
{
- //TODO: This code is got from UserStore.UpdateAsync and revised Update method based on that, but we should be sure that it's valid
- //Context.Attach(user);
- //user.ConcurrencyStamp = Guid.NewGuid().ToString();
- //Context.Update(user);
-
- DbContext.Attach(entity); //TODO: What is different for DbSet.Attach(entity)?
+ DbContext.Attach(entity);
if (entity is IHasConcurrencyStamp)
{
(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)
diff --git a/src/Volo.Abp/Volo/Abp/Domain/Entities/DefaultIdGenerator.cs b/src/Volo.Abp/Volo/Abp/Domain/Entities/DefaultIdGenerator.cs
new file mode 100644
index 0000000000..bb075812c1
--- /dev/null
+++ b/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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp/Volo/Abp/Domain/Entities/IdGenerator.cs b/src/Volo.Abp/Volo/Abp/Domain/Entities/IdGenerator.cs
new file mode 100644
index 0000000000..4c629874c0
--- /dev/null
+++ b/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();
+ }
+}
diff --git a/src/Volo.Abp/Volo/Abp/Guids/IGuidGenerator.cs b/src/Volo.Abp/Volo/Abp/Guids/IGuidGenerator.cs
new file mode 100644
index 0000000000..fcddc00e10
--- /dev/null
+++ b/src/Volo.Abp/Volo/Abp/Guids/IGuidGenerator.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Volo.Abp.Guids
+{
+ ///
+ /// Used to generate Ids.
+ ///
+ public interface IGuidGenerator
+ {
+ ///
+ /// Creates a new .
+ ///
+ Guid Create();
+ }
+}
diff --git a/src/Volo.Abp/Volo/Abp/Guids/SequentialGuidGenerator.cs b/src/Volo.Abp/Volo/Abp/Guids/SequentialGuidGenerator.cs
new file mode 100644
index 0000000000..22db24cde2
--- /dev/null
+++ b/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 */
+
+ ///
+ /// Implements by creating sequential Guids.
+ /// Use to configure.
+ ///
+ public class SequentialGuidGenerator : IGuidGenerator
+ {
+ public SequentialGuidGeneratorOptions Options { get; }
+
+ private static readonly RandomNumberGenerator RandomNumberGenerator = RandomNumberGenerator.Create();
+
+ public SequentialGuidGenerator(IOptions 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp/Volo/Abp/Guids/SequentialGuidGeneratorOptions.cs b/src/Volo.Abp/Volo/Abp/Guids/SequentialGuidGeneratorOptions.cs
new file mode 100644
index 0000000000..913a7a059b
--- /dev/null
+++ b/src/Volo.Abp/Volo/Abp/Guids/SequentialGuidGeneratorOptions.cs
@@ -0,0 +1,15 @@
+namespace Volo.Abp.Guids
+{
+ public class SequentialGuidGeneratorOptions
+ {
+ ///
+ /// Default value: .
+ ///
+ public SequentialGuidType DefaultSequentialGuidType { get; set; }
+
+ public SequentialGuidGeneratorOptions()
+ {
+ DefaultSequentialGuidType = SequentialGuidType.SequentialAtEnd;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp/Volo/Abp/Guids/SequentialGuidType.cs b/src/Volo.Abp/Volo/Abp/Guids/SequentialGuidType.cs
new file mode 100644
index 0000000000..bc83923359
--- /dev/null
+++ b/src/Volo.Abp/Volo/Abp/Guids/SequentialGuidType.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Volo.Abp.Guids
+{
+ ///
+ /// Describes the type of a sequential GUID value.
+ ///
+ public enum SequentialGuidType
+ {
+ ///
+ /// The GUID should be sequential when formatted using the method.
+ /// Used by MySql and PostgreSql.
+ ///
+ SequentialAsString,
+
+ ///
+ /// The GUID should be sequential when formatted using the method.
+ /// Used by Oracle.
+ ///
+ SequentialAsBinary,
+
+ ///
+ /// The sequential portion of the GUID should be located at the end of the Data4 block.
+ /// Used by SqlServer.
+ ///
+ SequentialAtEnd
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp/Volo/Abp/Guids/SimpleGuidGenerator.cs b/src/Volo.Abp/Volo/Abp/Guids/SimpleGuidGenerator.cs
new file mode 100644
index 0000000000..265cd52d81
--- /dev/null
+++ b/src/Volo.Abp/Volo/Abp/Guids/SimpleGuidGenerator.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Volo.Abp.Guids
+{
+ ///
+ /// Implements by using .
+ ///
+ public class SimpleGuidGenerator : IGuidGenerator
+ {
+ public virtual Guid Create()
+ {
+ return Guid.NewGuid();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp/Volo/Abp/Threading/LockExtensions.cs b/src/Volo.Abp/Volo/Abp/Threading/LockExtensions.cs
new file mode 100644
index 0000000000..463cca7cc5
--- /dev/null
+++ b/src/Volo.Abp/Volo/Abp/Threading/LockExtensions.cs
@@ -0,0 +1,68 @@
+using System;
+
+namespace Volo.Abp.Threading
+{
+ ///
+ /// Extension methods to make locking easier.
+ ///
+ public static class LockExtensions
+ {
+ ///
+ /// Executes given by locking given object.
+ ///
+ /// Source object (to be locked)
+ /// Action (to be executed)
+ public static void Locking(this object source, Action action)
+ {
+ lock (source)
+ {
+ action();
+ }
+ }
+
+ ///
+ /// Executes given by locking given object.
+ ///
+ /// Type of the object (to be locked)
+ /// Source object (to be locked)
+ /// Action (to be executed)
+ public static void Locking(this T source, Action action) where T : class
+ {
+ lock (source)
+ {
+ action(source);
+ }
+ }
+
+ ///
+ /// Executes given and returns it's value by locking given object.
+ ///
+ /// Return type
+ /// Source object (to be locked)
+ /// Function (to be executed)
+ /// Return value of the
+ public static TResult Locking(this object source, Func func)
+ {
+ lock (source)
+ {
+ return func();
+ }
+ }
+
+ ///
+ /// Executes given and returns it's value by locking given object.
+ ///
+ /// Type of the object (to be locked)
+ /// Return type
+ /// Source object (to be locked)
+ /// Function (to be executed)
+ /// Return value of the
+ public static TResult Locking(this T source, Func func) where T : class
+ {
+ lock (source)
+ {
+ return func(source);
+ }
+ }
+ }
+}