From a88e53e20858d2580abfe81c35f6e4e537667e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A3=D0=BB=D1=8C=D1=8F=D0=BD=D0=BE=D0=B2=20=D0=92=D0=BB?= =?UTF-8?q?=D0=B0=D0=B4=D0=B8=D1=81=D0=BB=D0=B0=D0=B2?= Date: Thu, 6 Oct 2022 14:58:31 +0300 Subject: [PATCH 1/3] Thread safe SqlGenerator --- DapperExtensions/DapperAsyncExtensions.cs | 2 +- DapperExtensions/DapperExtensions.cs | 2 +- .../PooledObjectPolicy.cs | 18 + .../Sql/ThreadSafeSqlGeneratorImpl.cs | 307 ++++++++++++++++++ 4 files changed, 327 insertions(+), 2 deletions(-) create mode 100644 DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs create mode 100644 DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs diff --git a/DapperExtensions/DapperAsyncExtensions.cs b/DapperExtensions/DapperAsyncExtensions.cs index 620d5ca1..3966d5d5 100644 --- a/DapperExtensions/DapperAsyncExtensions.cs +++ b/DapperExtensions/DapperAsyncExtensions.cs @@ -60,7 +60,7 @@ public static Func Inst { get { - return _instanceFactory ??= config => new DapperAsyncImplementor(new SqlGeneratorImpl(_configuration)); + return _instanceFactory ??= config => new DapperAsyncImplementor(new ThreadSafeSqlGeneratorImpl(_configuration)); } set { diff --git a/DapperExtensions/DapperExtensions.cs b/DapperExtensions/DapperExtensions.cs index 3797587e..c11f8d95 100644 --- a/DapperExtensions/DapperExtensions.cs +++ b/DapperExtensions/DapperExtensions.cs @@ -59,7 +59,7 @@ public static Func InstanceF { get { - return _instanceFactory ??= config => new DapperImplementor(new SqlGeneratorImpl(config)); + return _instanceFactory ??= config => new DapperImplementor(new ThreadSafeSqlGeneratorImpl(config)); } set { diff --git a/DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs b/DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs new file mode 100644 index 00000000..816abed5 --- /dev/null +++ b/DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Extensions.ObjectPool +{ + /// + /// Represents a policy for managing pooled objects. + /// + /// The type of object which is being pooled. + public abstract class PooledObjectPolicy where T : notnull + { + /// + public abstract T Create(); + + /// + public abstract bool Return(T obj); + } +} diff --git a/DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs b/DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs new file mode 100644 index 00000000..cf6dec86 --- /dev/null +++ b/DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using DapperExtensions.Mapper; +using DapperExtensions.Predicate; +using Microsoft.Extensions.ObjectPool; + +namespace DapperExtensions.Sql +{ + public class ThreadSafeSqlGeneratorImpl : ISqlGenerator + { + private readonly ObjectPool pool; + + public ThreadSafeSqlGeneratorImpl(IDapperExtensionsConfiguration configuration) + { + this.Configuration = configuration; + this.pool = new ObjectPool(new DefaultPoolPolicy(configuration), Environment.ProcessorCount * 2); + } + + public IDapperExtensionsConfiguration Configuration { get; } + + public IList AllColumns + { + get + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.AllColumns; + } + finally + { + pool.Return(sqlGenerator); + } + } + } + + public IList MappedTables + { + get + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.MappedTables; + } + finally + { + pool.Return(sqlGenerator); + } + } + } + + public bool SupportsMultipleStatements() + { + return this.Configuration.Dialect.SupportsMultipleStatements; + } + + + public string Select(IClassMapper classMap, IPredicate predicate, IList sort, + IDictionary parameters, IList colsToSelect, + IList includedProperties = null) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.Select(classMap, predicate, sort, parameters, colsToSelect, includedProperties); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string SelectPaged(IClassMapper classMap, IPredicate predicate, IList sort, int page, + int resultsPerPage, + IDictionary parameters, IList colsToSelect, + IList includedProperties = null) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.SelectPaged(classMap, predicate, sort, page, resultsPerPage, parameters, + colsToSelect, includedProperties); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string SelectSet(IClassMapper classMap, IPredicate predicate, IList sort, int firstResult, + int maxResults, + IDictionary parameters, IList colsToSelect, + IList includedProperties = null) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.SelectSet(classMap, predicate, sort, firstResult, maxResults, parameters, + colsToSelect, includedProperties); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string Count(IClassMapper classMap, IPredicate predicate, IDictionary parameters, + IList includedProperties = null) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.Count(classMap, predicate, parameters, includedProperties); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string Insert(IClassMapper classMap) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.Insert(classMap); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string Update(IClassMapper classMap, IPredicate predicate, IDictionary parameters, + bool ignoreAllKeyProperties, + IList colsToUpdate) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.Update(classMap, predicate, parameters, ignoreAllKeyProperties, colsToUpdate); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string Delete(IClassMapper classMap, IPredicate predicate, IDictionary parameters) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.Delete(classMap, predicate, parameters); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string IdentitySql(IClassMapper classMap) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.IdentitySql(classMap); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string GetTableName(IClassMapper map, bool useAlias = false) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.GetTableName(map, useAlias); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string GetColumnName(IClassMapper map, IMemberMap property, bool includeAlias, bool isDml = false, + bool includePrefix = true) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.GetColumnName(map, property, includeAlias, isDml, includePrefix); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string GetColumnName(IClassMapper map, string propertyName, bool includeAlias, bool includePrefix = true) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.GetColumnName(map, propertyName, includeAlias, includePrefix); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string GetColumnName(IColumn column, bool includeAlias, bool includePrefix = true) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.GetColumnName(column, includeAlias, includePrefix); + } + finally + { + pool.Return(sqlGenerator); + } + } + } + + public sealed class DefaultPoolPolicy : PooledObjectPolicy + { + private readonly IDapperExtensionsConfiguration _configuration; + + public DefaultPoolPolicy(IDapperExtensionsConfiguration configuration) + { + _configuration = configuration; + } + + public override ISqlGenerator Create() + { + return new SqlGeneratorImpl(_configuration); + } + + public override bool Return(ISqlGenerator obj) + { + return true; + } + } + + public class ObjectPool + { + protected readonly ISqlGenerator[] _items; + protected ISqlGenerator? _firstItem; + private protected readonly PooledObjectPolicy _policy; + + public ObjectPool(PooledObjectPolicy policy, int maximumRetained) + { + this._policy = policy ?? throw new ArgumentNullException(nameof(policy)); + // -1 due to _firstItem + _items = new ISqlGenerator[maximumRetained - 1]; + } + + public ISqlGenerator Get() + { + var item = _firstItem; + if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item) + { + var items = _items; + for (var i = 0; i < items.Length; i++) + { + item = items[i]; + if (item != null && Interlocked.CompareExchange(ref items[i], null, item) == item) + { + return item; + } + } + + item = Create(); + } + + return item; + } + + /// + public void Return(ISqlGenerator obj) + { + if (_policy.Return(obj)) + { + if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null) + { + var items = _items; + for (var i = 0; + i < items.Length && Interlocked.CompareExchange(ref items[i], obj, null) != null; + ++i) + { + } + } + } + } + + // Non-inline to improve its code quality as uncommon path + [MethodImpl(MethodImplOptions.NoInlining)] + protected virtual ISqlGenerator Create() => _policy.Create(); + } +} \ No newline at end of file From c5947387eee41fd8608a89834269b1a59c4d234d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A3=D0=BB=D1=8C=D1=8F=D0=BD=D0=BE=D0=B2=20=D0=92=D0=BB?= =?UTF-8?q?=D0=B0=D0=B4=D0=B8=D1=81=D0=BB=D0=B0=D0=B2?= Date: Thu, 6 Oct 2022 14:58:31 +0300 Subject: [PATCH 2/3] Thread safe SqlGenerator --- .../DapperExtensions.StrongName.csproj | 49 +-- DapperExtensions/DapperAsyncExtensions.cs | 2 +- DapperExtensions/DapperExtensions.cs | 2 +- .../PooledObjectPolicy.cs | 18 + .../Sql/ThreadSafeSqlGeneratorImpl.cs | 307 ++++++++++++++++++ 5 files changed, 328 insertions(+), 50 deletions(-) create mode 100644 DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs create mode 100644 DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs diff --git a/DapperExtensions.StrongName/DapperExtensions.StrongName.csproj b/DapperExtensions.StrongName/DapperExtensions.StrongName.csproj index 45853560..31adf7ee 100644 --- a/DapperExtensions.StrongName/DapperExtensions.StrongName.csproj +++ b/DapperExtensions.StrongName/DapperExtensions.StrongName.csproj @@ -94,53 +94,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/DapperExtensions/DapperAsyncExtensions.cs b/DapperExtensions/DapperAsyncExtensions.cs index 620d5ca1..3966d5d5 100644 --- a/DapperExtensions/DapperAsyncExtensions.cs +++ b/DapperExtensions/DapperAsyncExtensions.cs @@ -60,7 +60,7 @@ public static Func Inst { get { - return _instanceFactory ??= config => new DapperAsyncImplementor(new SqlGeneratorImpl(_configuration)); + return _instanceFactory ??= config => new DapperAsyncImplementor(new ThreadSafeSqlGeneratorImpl(_configuration)); } set { diff --git a/DapperExtensions/DapperExtensions.cs b/DapperExtensions/DapperExtensions.cs index 3797587e..c11f8d95 100644 --- a/DapperExtensions/DapperExtensions.cs +++ b/DapperExtensions/DapperExtensions.cs @@ -59,7 +59,7 @@ public static Func InstanceF { get { - return _instanceFactory ??= config => new DapperImplementor(new SqlGeneratorImpl(config)); + return _instanceFactory ??= config => new DapperImplementor(new ThreadSafeSqlGeneratorImpl(config)); } set { diff --git a/DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs b/DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs new file mode 100644 index 00000000..816abed5 --- /dev/null +++ b/DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Extensions.ObjectPool +{ + /// + /// Represents a policy for managing pooled objects. + /// + /// The type of object which is being pooled. + public abstract class PooledObjectPolicy where T : notnull + { + /// + public abstract T Create(); + + /// + public abstract bool Return(T obj); + } +} diff --git a/DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs b/DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs new file mode 100644 index 00000000..cf6dec86 --- /dev/null +++ b/DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using DapperExtensions.Mapper; +using DapperExtensions.Predicate; +using Microsoft.Extensions.ObjectPool; + +namespace DapperExtensions.Sql +{ + public class ThreadSafeSqlGeneratorImpl : ISqlGenerator + { + private readonly ObjectPool pool; + + public ThreadSafeSqlGeneratorImpl(IDapperExtensionsConfiguration configuration) + { + this.Configuration = configuration; + this.pool = new ObjectPool(new DefaultPoolPolicy(configuration), Environment.ProcessorCount * 2); + } + + public IDapperExtensionsConfiguration Configuration { get; } + + public IList AllColumns + { + get + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.AllColumns; + } + finally + { + pool.Return(sqlGenerator); + } + } + } + + public IList
MappedTables + { + get + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.MappedTables; + } + finally + { + pool.Return(sqlGenerator); + } + } + } + + public bool SupportsMultipleStatements() + { + return this.Configuration.Dialect.SupportsMultipleStatements; + } + + + public string Select(IClassMapper classMap, IPredicate predicate, IList sort, + IDictionary parameters, IList colsToSelect, + IList includedProperties = null) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.Select(classMap, predicate, sort, parameters, colsToSelect, includedProperties); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string SelectPaged(IClassMapper classMap, IPredicate predicate, IList sort, int page, + int resultsPerPage, + IDictionary parameters, IList colsToSelect, + IList includedProperties = null) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.SelectPaged(classMap, predicate, sort, page, resultsPerPage, parameters, + colsToSelect, includedProperties); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string SelectSet(IClassMapper classMap, IPredicate predicate, IList sort, int firstResult, + int maxResults, + IDictionary parameters, IList colsToSelect, + IList includedProperties = null) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.SelectSet(classMap, predicate, sort, firstResult, maxResults, parameters, + colsToSelect, includedProperties); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string Count(IClassMapper classMap, IPredicate predicate, IDictionary parameters, + IList includedProperties = null) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.Count(classMap, predicate, parameters, includedProperties); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string Insert(IClassMapper classMap) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.Insert(classMap); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string Update(IClassMapper classMap, IPredicate predicate, IDictionary parameters, + bool ignoreAllKeyProperties, + IList colsToUpdate) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.Update(classMap, predicate, parameters, ignoreAllKeyProperties, colsToUpdate); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string Delete(IClassMapper classMap, IPredicate predicate, IDictionary parameters) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.Delete(classMap, predicate, parameters); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string IdentitySql(IClassMapper classMap) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.IdentitySql(classMap); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string GetTableName(IClassMapper map, bool useAlias = false) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.GetTableName(map, useAlias); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string GetColumnName(IClassMapper map, IMemberMap property, bool includeAlias, bool isDml = false, + bool includePrefix = true) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.GetColumnName(map, property, includeAlias, isDml, includePrefix); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string GetColumnName(IClassMapper map, string propertyName, bool includeAlias, bool includePrefix = true) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.GetColumnName(map, propertyName, includeAlias, includePrefix); + } + finally + { + pool.Return(sqlGenerator); + } + } + + public string GetColumnName(IColumn column, bool includeAlias, bool includePrefix = true) + { + var sqlGenerator = pool.Get(); + try + { + return sqlGenerator.GetColumnName(column, includeAlias, includePrefix); + } + finally + { + pool.Return(sqlGenerator); + } + } + } + + public sealed class DefaultPoolPolicy : PooledObjectPolicy + { + private readonly IDapperExtensionsConfiguration _configuration; + + public DefaultPoolPolicy(IDapperExtensionsConfiguration configuration) + { + _configuration = configuration; + } + + public override ISqlGenerator Create() + { + return new SqlGeneratorImpl(_configuration); + } + + public override bool Return(ISqlGenerator obj) + { + return true; + } + } + + public class ObjectPool + { + protected readonly ISqlGenerator[] _items; + protected ISqlGenerator? _firstItem; + private protected readonly PooledObjectPolicy _policy; + + public ObjectPool(PooledObjectPolicy policy, int maximumRetained) + { + this._policy = policy ?? throw new ArgumentNullException(nameof(policy)); + // -1 due to _firstItem + _items = new ISqlGenerator[maximumRetained - 1]; + } + + public ISqlGenerator Get() + { + var item = _firstItem; + if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item) + { + var items = _items; + for (var i = 0; i < items.Length; i++) + { + item = items[i]; + if (item != null && Interlocked.CompareExchange(ref items[i], null, item) == item) + { + return item; + } + } + + item = Create(); + } + + return item; + } + + /// + public void Return(ISqlGenerator obj) + { + if (_policy.Return(obj)) + { + if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null) + { + var items = _items; + for (var i = 0; + i < items.Length && Interlocked.CompareExchange(ref items[i], obj, null) != null; + ++i) + { + } + } + } + } + + // Non-inline to improve its code quality as uncommon path + [MethodImpl(MethodImplOptions.NoInlining)] + protected virtual ISqlGenerator Create() => _policy.Create(); + } +} \ No newline at end of file From 162d884c5f566a10b3e8ed5c238912e0418f532d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A3=D0=BB=D1=8C=D1=8F=D0=BD=D0=BE=D0=B2=20=D0=92=D0=BB?= =?UTF-8?q?=D0=B0=D0=B4=D0=B8=D1=81=D0=BB=D0=B0=D0=B2?= Date: Thu, 13 Apr 2023 16:58:44 +0300 Subject: [PATCH 3/3] ThreadSafeSqlGeneratorImpl.cs rework --- .../Async/DatabaseAsyncTestsFixture.cs | 2 +- .../Async/Sqlite/CrudFixture.cs | 34 +++ .../PooledObjectPolicy.cs | 18 -- .../Sql/ThreadSafeSqlGeneratorImpl.cs | 239 ++---------------- 4 files changed, 56 insertions(+), 237 deletions(-) delete mode 100644 DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs diff --git a/DapperExtensions.Test/IntegrationTests/Async/DatabaseAsyncTestsFixture.cs b/DapperExtensions.Test/IntegrationTests/Async/DatabaseAsyncTestsFixture.cs index 6018a39c..d2569ad4 100644 --- a/DapperExtensions.Test/IntegrationTests/Async/DatabaseAsyncTestsFixture.cs +++ b/DapperExtensions.Test/IntegrationTests/Async/DatabaseAsyncTestsFixture.cs @@ -19,7 +19,7 @@ protected DatabaseAsyncTestsFixture(string configPath = null) : base(configPath) protected override void CommonSetup(DbConnection connection, SqlDialectBase sqlDialect) { var config = DapperAsyncExtensions.Configure(typeof(AutoClassMapper<>), new List(), sqlDialect); - var sqlGenerator = new SqlGeneratorImpl(config); + var sqlGenerator = new ThreadSafeSqlGeneratorImpl(config); Db = new AsyncDatabase(connection, sqlGenerator); } diff --git a/DapperExtensions.Test/IntegrationTests/Async/Sqlite/CrudFixture.cs b/DapperExtensions.Test/IntegrationTests/Async/Sqlite/CrudFixture.cs index caba104a..7d44b199 100644 --- a/DapperExtensions.Test/IntegrationTests/Async/Sqlite/CrudFixture.cs +++ b/DapperExtensions.Test/IntegrationTests/Async/Sqlite/CrudFixture.cs @@ -245,6 +245,40 @@ public void UsingCompositeKey_UpdatesEntity() Assert.AreEqual("key", m3.Key2); Assert.AreEqual("barz", m3.Value); } + + [Test] + public async Task AsyncBatch_UsingKey_UpdatesEntity() + { + Person p1 = new Person + { + Active = true, + FirstName = "Foo", + LastName = "Bar", + DateCreated = DateTime.UtcNow + }; + var animal = new Animal + { + Name = "Fizz" + }; + p1.Id = await Db.Insert(p1); + animal.Id = await Db.Insert(animal); + + p1.FirstName = "Baz"; + p1.Active = false; + + animal.Name = "Buzz"; + + var task1 = Task.Run(() => Db.Update(p1, ignoreAllKeyProperties: true)); + var task2 = Task.Run(() => Db.Update(animal, ignoreAllKeyProperties: true)); + await Task.WhenAll(task1, task2); + + var p1_3 = await Db.Get(p1.Id); + Assert.AreEqual("Baz", p1_3.FirstName); + Assert.AreEqual("Bar", p1_3.LastName); + Assert.AreEqual(false, p1_3.Active); + var a2 = await Db.Get(animal.Id); + Assert.AreEqual("Buzz", a2.Name); + } } [TestFixture] diff --git a/DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs b/DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs deleted file mode 100644 index 816abed5..00000000 --- a/DapperExtensions/Microsoft.Externsions.ObjectPool/PooledObjectPolicy.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.Extensions.ObjectPool -{ - /// - /// Represents a policy for managing pooled objects. - /// - /// The type of object which is being pooled. - public abstract class PooledObjectPolicy where T : notnull - { - /// - public abstract T Create(); - - /// - public abstract bool Return(T obj); - } -} diff --git a/DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs b/DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs index cf6dec86..42b86dc5 100644 --- a/DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs +++ b/DapperExtensions/Sql/ThreadSafeSqlGeneratorImpl.cs @@ -1,56 +1,29 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; using System.Threading; using DapperExtensions.Mapper; using DapperExtensions.Predicate; -using Microsoft.Extensions.ObjectPool; namespace DapperExtensions.Sql { public class ThreadSafeSqlGeneratorImpl : ISqlGenerator { - private readonly ObjectPool pool; + [ThreadStatic] + private static SqlGeneratorImpl _sqlGeneratorImpl; + + private SqlGeneratorImpl Current => LazyInitializer.EnsureInitialized(ref _sqlGeneratorImpl, () => new SqlGeneratorImpl(Configuration)); + public ThreadSafeSqlGeneratorImpl(IDapperExtensionsConfiguration configuration) { this.Configuration = configuration; - this.pool = new ObjectPool(new DefaultPoolPolicy(configuration), Environment.ProcessorCount * 2); } public IDapperExtensionsConfiguration Configuration { get; } - public IList AllColumns - { - get - { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.AllColumns; - } - finally - { - pool.Return(sqlGenerator); - } - } - } + public IList AllColumns => Current.AllColumns; - public IList
MappedTables - { - get - { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.MappedTables; - } - finally - { - pool.Return(sqlGenerator); - } - } - } + public IList
MappedTables => Current.MappedTables; public bool SupportsMultipleStatements() { @@ -62,15 +35,7 @@ public string Select(IClassMapper classMap, IPredicate predicate, IList s IDictionary parameters, IList colsToSelect, IList includedProperties = null) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.Select(classMap, predicate, sort, parameters, colsToSelect, includedProperties); - } - finally - { - pool.Return(sqlGenerator); - } + return this.Current.Select(classMap, predicate, sort, parameters, colsToSelect, includedProperties); } public string SelectPaged(IClassMapper classMap, IPredicate predicate, IList sort, int page, @@ -78,16 +43,9 @@ public string SelectPaged(IClassMapper classMap, IPredicate predicate, IList parameters, IList colsToSelect, IList includedProperties = null) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.SelectPaged(classMap, predicate, sort, page, resultsPerPage, parameters, + return this.Current.SelectPaged(classMap, predicate, sort, page, resultsPerPage, parameters, colsToSelect, includedProperties); - } - finally - { - pool.Return(sqlGenerator); - } + } public string SelectSet(IClassMapper classMap, IPredicate predicate, IList sort, int firstResult, @@ -95,213 +53,58 @@ public string SelectSet(IClassMapper classMap, IPredicate predicate, IList parameters, IList colsToSelect, IList includedProperties = null) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.SelectSet(classMap, predicate, sort, firstResult, maxResults, parameters, + return this.Current.SelectSet(classMap, predicate, sort, firstResult, maxResults, parameters, colsToSelect, includedProperties); - } - finally - { - pool.Return(sqlGenerator); - } + } public string Count(IClassMapper classMap, IPredicate predicate, IDictionary parameters, IList includedProperties = null) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.Count(classMap, predicate, parameters, includedProperties); - } - finally - { - pool.Return(sqlGenerator); - } + return this.Current.Count(classMap, predicate, parameters, includedProperties); } public string Insert(IClassMapper classMap) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.Insert(classMap); - } - finally - { - pool.Return(sqlGenerator); - } + return this.Current.Insert(classMap); } public string Update(IClassMapper classMap, IPredicate predicate, IDictionary parameters, bool ignoreAllKeyProperties, IList colsToUpdate) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.Update(classMap, predicate, parameters, ignoreAllKeyProperties, colsToUpdate); - } - finally - { - pool.Return(sqlGenerator); - } + return this.Current.Update(classMap, predicate, parameters, ignoreAllKeyProperties, colsToUpdate); } public string Delete(IClassMapper classMap, IPredicate predicate, IDictionary parameters) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.Delete(classMap, predicate, parameters); - } - finally - { - pool.Return(sqlGenerator); - } + return this.Current.Delete(classMap, predicate, parameters); } public string IdentitySql(IClassMapper classMap) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.IdentitySql(classMap); - } - finally - { - pool.Return(sqlGenerator); - } + return this.Current.IdentitySql(classMap); } public string GetTableName(IClassMapper map, bool useAlias = false) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.GetTableName(map, useAlias); - } - finally - { - pool.Return(sqlGenerator); - } + return this.Current.GetTableName(map, useAlias); } public string GetColumnName(IClassMapper map, IMemberMap property, bool includeAlias, bool isDml = false, bool includePrefix = true) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.GetColumnName(map, property, includeAlias, isDml, includePrefix); - } - finally - { - pool.Return(sqlGenerator); - } + return this.Current.GetColumnName(map, property, includeAlias, isDml, includePrefix); } public string GetColumnName(IClassMapper map, string propertyName, bool includeAlias, bool includePrefix = true) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.GetColumnName(map, propertyName, includeAlias, includePrefix); - } - finally - { - pool.Return(sqlGenerator); - } + return this.Current.GetColumnName(map, propertyName, includeAlias, includePrefix); } public string GetColumnName(IColumn column, bool includeAlias, bool includePrefix = true) { - var sqlGenerator = pool.Get(); - try - { - return sqlGenerator.GetColumnName(column, includeAlias, includePrefix); - } - finally - { - pool.Return(sqlGenerator); - } - } - } - - public sealed class DefaultPoolPolicy : PooledObjectPolicy - { - private readonly IDapperExtensionsConfiguration _configuration; - - public DefaultPoolPolicy(IDapperExtensionsConfiguration configuration) - { - _configuration = configuration; - } - - public override ISqlGenerator Create() - { - return new SqlGeneratorImpl(_configuration); - } - - public override bool Return(ISqlGenerator obj) - { - return true; - } - } - - public class ObjectPool - { - protected readonly ISqlGenerator[] _items; - protected ISqlGenerator? _firstItem; - private protected readonly PooledObjectPolicy _policy; - - public ObjectPool(PooledObjectPolicy policy, int maximumRetained) - { - this._policy = policy ?? throw new ArgumentNullException(nameof(policy)); - // -1 due to _firstItem - _items = new ISqlGenerator[maximumRetained - 1]; - } - - public ISqlGenerator Get() - { - var item = _firstItem; - if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item) - { - var items = _items; - for (var i = 0; i < items.Length; i++) - { - item = items[i]; - if (item != null && Interlocked.CompareExchange(ref items[i], null, item) == item) - { - return item; - } - } - - item = Create(); - } - - return item; - } - - /// - public void Return(ISqlGenerator obj) - { - if (_policy.Return(obj)) - { - if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null) - { - var items = _items; - for (var i = 0; - i < items.Length && Interlocked.CompareExchange(ref items[i], obj, null) != null; - ++i) - { - } - } - } + return this.Current.GetColumnName(column, includeAlias, includePrefix); } - - // Non-inline to improve its code quality as uncommon path - [MethodImpl(MethodImplOptions.NoInlining)] - protected virtual ISqlGenerator Create() => _policy.Create(); } } \ No newline at end of file