diff --git a/README.md b/README.md index 2df7945..f754a52 100644 --- a/README.md +++ b/README.md @@ -744,6 +744,41 @@ await database.UpsertAsync(people); ``` + +#### Execute with temp table + +Kros.KORM offers special execute commands for SQL databases, that inserts provided simple data into temp table and +then executes some specified action using those temporary data. +You can find these extension methods in `IDatabaseExtensions` class. + +```CSharp +database.ExecuteWithTempTable(IEnumerable values, action); +await database.ExecuteWithTempTableAsync(IEnumerable values, function); + +database.ExecuteWithTempTable(IDictionary values, action); +await database.ExecuteWithTempTable(IDictionary values, function); + +T database.ExecuteWithTempTable (IEnumerable values, action); +await T database.ExecuteWithTempTable (IEnumerable values, function); + +T database.ExecuteWithTempTable (IDictionary values, action); +await T database.ExecuteWithTempTable (IDictionary values, function); + +public class Person +{ + public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } +} + +var ids = new List(){ 0, 1, 2, 3 }; + +_database.ExecuteWithTempTable(ids, (database, tableName) + => database.Query() + .From($"PERSON AS P INNER JOIN {tableName} AS T ON (P.Id = T.Value)") + .ToList()); +``` + ### SQL commands executing Kros.KORM supports SQL commands execution. There are three types of commands: diff --git a/src/Extensions/TypeExtensions.cs b/src/Extensions/TypeExtensions.cs new file mode 100644 index 0000000..5769643 --- /dev/null +++ b/src/Extensions/TypeExtensions.cs @@ -0,0 +1,117 @@ +using Kros.KORM.Properties; +using System; +using System.Collections.Generic; +using System.Data; + +namespace Kros.KORM.Extensions +{ + /// + /// .NET clr type extensions. + /// + internal static class TypeExtensions + { + private static readonly Dictionary _dbTypeMap = new() + { + { typeof(bool), DbType.Boolean }, + { typeof(bool?), DbType.Boolean }, + { typeof(byte[]), DbType.Binary }, + { typeof(byte), DbType.Byte }, + { typeof(byte?), DbType.Byte }, + { typeof(sbyte), DbType.SByte }, + { typeof(sbyte?), DbType.SByte }, + { typeof(short), DbType.Int16 }, + { typeof(short?), DbType.Int16 }, + { typeof(ushort), DbType.UInt16 }, + { typeof(ushort?), DbType.UInt16 }, + { typeof(int), DbType.Int32 }, + { typeof(int?), DbType.Int32 }, + { typeof(uint), DbType.UInt32 }, + { typeof(uint?), DbType.UInt32 }, + { typeof(long), DbType.Int64 }, + { typeof(long?), DbType.Int64 }, + { typeof(ulong), DbType.UInt64 }, + { typeof(ulong?), DbType.UInt64 }, + { typeof(float), DbType.Single }, + { typeof(float?), DbType.Single }, + { typeof(decimal), DbType.Decimal }, + { typeof(decimal?), DbType.Decimal }, + { typeof(double), DbType.Double }, + { typeof(double?), DbType.Double }, + { typeof(DateTime), DbType.DateTime }, + { typeof(DateTime?), DbType.DateTime }, + { typeof(Guid), DbType.Guid }, + { typeof(object), DbType.Binary }, + { typeof(string), DbType.String } + }; + + private static readonly Dictionary _sqlTypeMap = new() + { + { typeof(bool), "bit" }, + { typeof(bool?), "bit" }, + { typeof(byte[]), "varBinary" }, + { typeof(byte), "tinyInt" }, + { typeof(byte?), "tinyInt" }, + { typeof(sbyte), "tinyInt" }, + { typeof(sbyte?), "tinyInt" }, + { typeof(short), "smallInt" }, + { typeof(short?), "smallInt" }, + { typeof(ushort), "smallInt" }, + { typeof(ushort?), "smallInt" }, + { typeof(int), "int" }, + { typeof(int?), "int" }, + { typeof(uint), "int" }, + { typeof(uint?), "int" }, + { typeof(long), "bigInt" }, + { typeof(long?), "bigInt" }, + { typeof(ulong), "bigInt" }, + { typeof(ulong?), "bigInt" }, + { typeof(float), "real" }, + { typeof(float?), "real" }, + { typeof(decimal), "decimal" }, + { typeof(decimal?), "decimal" }, + { typeof(double), "float" }, + { typeof(double?), "float" }, + { typeof(DateTime), "dateTime" }, + { typeof(DateTime?), "dateTime" }, + { typeof(Guid), "uniqueIdentifier" }, + { typeof(object), "varBinary" }, + { typeof(string), "nVarChar(255)" } + }; + + /// + /// Converts .NET clr type to DbType. + /// + /// Clr type. + /// DbType. + /// When clr type is not supported. + public static DbType ToDbType(this Type type) + { + if (type.IsEnum) + { + type = Enum.GetUnderlyingType(type); + } + + return _dbTypeMap.TryGetValue(type, out DbType dbType) + ? dbType + : throw new NotSupportedException(string.Format(Resources.ConversionFromTypeIsNotSupported, type.Name)); + } + + /// + /// Converts .NET clr type to SQL data type. + /// + /// Clr type. + /// SQL data type. + /// When clr type is not supported. + public static string ToSqlDataType(this Type type) + { + if (type.IsEnum) + { + type = Enum.GetUnderlyingType(type); + } + + return _sqlTypeMap.TryGetValue(type, out string sqlDataType) + ? sqlDataType + : throw new NotSupportedException(string.Format(Resources.ConversionFromTypeIsNotSupported, type.Name)); + } + } +} diff --git a/src/IDatabaseExtensions.TempTable.cs b/src/IDatabaseExtensions.TempTable.cs new file mode 100644 index 0000000..9041833 --- /dev/null +++ b/src/IDatabaseExtensions.TempTable.cs @@ -0,0 +1,264 @@ +using Kros.Data.BulkActions; +using Kros.KORM.Data; +using Kros.KORM.Extensions; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Kros.KORM +{ + public partial class IDatabaseExtensions + { + /// + /// Special execute command, that will insert into temp table + /// and then executes that is dependent on data in temp table. + /// + /// Type of values for inserting into temp table. + /// Korm database access. + /// Values for inserting into temp table. + /// Execute action. + /// Whole action is executed in transaction. + public static void ExecuteWithTempTable( + this IDatabase database, + IEnumerable values, + Action action) + { + using ITransaction transaction = database.BeginTransaction(); + + action(database, CreateTempTable(database, values)); + + TryCommitTransaction(transaction); + } + + /// + /// Special execute command, that will insert into temp table + /// and then executes that is dependent on data in temp table. + /// + /// Type of values for inserting into temp table. + /// Korm database access. + /// Values for inserting into temp table. + /// Execute action. + /// Whole action is executed in transaction and is asynchronous. + public static async Task ExecuteWithTempTableAsync( + this IDatabase database, + IEnumerable values, + Func actionAsync) + { + using ITransaction transaction = database.BeginTransaction(); + + await actionAsync(database, CreateTempTable(database, values)); + + TryCommitTransaction(transaction); + } + + /// + /// Special execute command, that will insert into temp table + /// and then executes that is dependent on data in temp table. + /// + /// Type of keys for inserting into temp table. + /// Type of values for inserting into temp table. + /// Korm database access. + /// Values for inserting into temp table. + /// Execute action. + /// Whole action is executed in transaction. + public static void ExecuteWithTempTable( + this IDatabase database, + IDictionary values, + Action action) + { + using ITransaction transaction = database.BeginTransaction(); + + action(database, CreateTempTable(database, values)); + + TryCommitTransaction(transaction); + } + + /// + /// Special execute command, that will insert into temp table + /// and then executes that is dependent on data in temp table. + /// + /// Type of keys for inserting into temp table. + /// Type of values for inserting into temp table. + /// Korm database access. + /// Values for inserting into temp table. + /// Execute action. + /// Whole action is executed in transaction and is asynchronous. + public static async Task ExecuteWithTempTableAsync( + this IDatabase database, + IDictionary values, + Func actionAsync) + { + using ITransaction transaction = database.BeginTransaction(); + + await actionAsync(database, CreateTempTable(database, values)); + + TryCommitTransaction(transaction); + } + + /// + /// Special execute command, that will insert into temp table + /// and then executes that is dependent on data in temp table. + /// + /// Return type. + /// Type of values for inserting into temp table. + /// Korm database access. + /// Values for inserting into temp table. + /// Execute action. + /// User defined type . + /// Whole action is executed in transaction. + public static T ExecuteWithTempTable( + this IDatabase database, + IEnumerable values, + Func action) + { + using ITransaction transaction = database.BeginTransaction(); + + T ret = action(database, CreateTempTable(database, values)); + + TryCommitTransaction(transaction); + + return ret; + } + + /// + /// Special execute command, that will insert into temp table + /// and then executes that is dependent on data in temp table. + /// + /// Return type. + /// Type of values for inserting into temp table. + /// Korm database access. + /// Values for inserting into temp table. + /// Execute action. + /// User defined type . + /// Whole action is executed in transaction and is asynchronous. + public static async Task ExecuteWithTempTableAsync( + this IDatabase database, + IEnumerable values, + Func> actionAsync) + { + using ITransaction transaction = database.BeginTransaction(); + + T ret = await actionAsync(database, CreateTempTable(database, values)); + + TryCommitTransaction(transaction); + + return ret; + } + + /// + /// Special execute command, that will insert into temp table + /// and then executes that is dependent on data in temp table. + /// + /// Return type. + /// Type of keys for inserting into temp table. + /// Type of values for inserting into temp table. + /// Korm database access. + /// Values for inserting into temp table. + /// Execute action. + /// User defined type . + /// Whole action is executed in transaction. + public static T ExecuteWithTempTable( + this IDatabase database, + IDictionary values, + Func action) + { + using ITransaction transaction = database.BeginTransaction(); + + T ret = action(database, CreateTempTable(database, values)); + + TryCommitTransaction(transaction); + + return ret; + } + + /// + /// Special execute command, that will insert into temp table + /// and then executes that is dependent on data in temp table. + /// + /// Return type. + /// Type of keys for inserting into temp table. + /// Type of values for inserting into temp table. + /// Korm database access. + /// Values for inserting into temp table. + /// Execute action. + /// User defined type . + /// Whole action is executed in transaction and is asynchronous. + public static async Task ExecuteWithTempTableAsync( + this IDatabase database, + IDictionary values, + Func> actionAsync) + { + using ITransaction transaction = database.BeginTransaction(); + + T ret = await actionAsync(database, CreateTempTable(database, values)); + + TryCommitTransaction(transaction); + + return ret; + } + + private static void TryCommitTransaction(ITransaction transaction) + { + try + { + transaction.Commit(); + } + catch + { + transaction.Rollback(); + throw; + } + } + + private static string CreateTempTable(IDatabase database, IEnumerable values) + { + string tempTableName = GetTempTableName(); + InsertValuesIntoTempTable(database, values, tempTableName); + + return tempTableName; + } + + private static string CreateTempTable(IDatabase database, IDictionary values) + { + string tempTableName = GetTempTableName(); + InsertValuesIntoTempTable(database, values, tempTableName); + + return tempTableName; + } + + private static string GetTempTableName() + => $"#tt_{Guid.NewGuid():N}"; + + private static void InsertValuesIntoTempTable( + IDatabase database, + IEnumerable values, + string tempTableName) + { + database.ExecuteNonQuery($"CREATE TABLE {tempTableName} ( Value {typeof(TValue).ToSqlDataType()} )"); + + using IBulkInsert bulkInsert = database.CreateBulkInsert(); + + bulkInsert.DestinationTableName = tempTableName; + + using var reader = new EnumerableDataReader(values, new string[] { "Value" }); + + bulkInsert.Insert(reader); + } + + private static void InsertValuesIntoTempTable( + IDatabase database, + IDictionary values, + string tempTableName) + { + database.ExecuteNonQuery($"CREATE TABLE {tempTableName} " + + $"( [Key] {typeof(TKey).ToSqlDataType()}, " + + $" [Value] {typeof(TValue).ToSqlDataType()} )"); + + using IBulkInsert bulkInsert = database.CreateBulkInsert(); + bulkInsert.DestinationTableName = tempTableName; + + using var reader = new EnumerableDataReader>(values, new string[] { "Key", "Value" }); + bulkInsert.Insert(reader); + } + } +} diff --git a/src/IDatabaseExtensions.cs b/src/IDatabaseExtensions.cs index feae5cb..6c5eb77 100644 --- a/src/IDatabaseExtensions.cs +++ b/src/IDatabaseExtensions.cs @@ -11,7 +11,7 @@ namespace Kros.KORM /// /// Extensions over . /// - public static class IDatabaseExtensions + public static partial class IDatabaseExtensions { /// /// Adds the to the database. diff --git a/src/Kros.KORM.csproj b/src/Kros.KORM.csproj index 19d981f..063fa07 100644 --- a/src/Kros.KORM.csproj +++ b/src/Kros.KORM.csproj @@ -2,7 +2,7 @@ netcoreapp2.1;net46 - 4.2.0 + 4.3.0 KROS a. s. KROS a. s. KORM is fast, easy to use, micro ORM tool (Kros Object Relation Mapper). diff --git a/src/Properties/Resources.Designer.cs b/src/Properties/Resources.Designer.cs index 27fa002..65299e8 100644 --- a/src/Properties/Resources.Designer.cs +++ b/src/Properties/Resources.Designer.cs @@ -177,6 +177,15 @@ internal static string ConstructorParameterDoesNotMatchProperty { } } + /// + /// Looks up a localized string similar to Conversion from type: '{0}' is not supported.. + /// + internal static string ConversionFromTypeIsNotSupported { + get { + return ResourceManager.GetString("ConversionFromTypeIsNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to Converter type must implement implement IConverter interface.. /// diff --git a/src/Properties/Resources.resx b/src/Properties/Resources.resx index e6b245a..4891af3 100644 --- a/src/Properties/Resources.resx +++ b/src/Properties/Resources.resx @@ -160,6 +160,10 @@ 0 - constructor parameter name 1 - model type + + Conversion from type: '{0}' is not supported. + 0 - type name + Converter type must implement implement IConverter interface. diff --git a/tests/Kros.KORM.UnitTests/Extensions/TypeExtensionsShould.cs b/tests/Kros.KORM.UnitTests/Extensions/TypeExtensionsShould.cs new file mode 100644 index 0000000..70338e7 --- /dev/null +++ b/tests/Kros.KORM.UnitTests/Extensions/TypeExtensionsShould.cs @@ -0,0 +1,81 @@ +using FluentAssertions; +using Kros.KORM.Extensions; +using System; +using System.Data; +using Xunit; + +namespace Kros.KORM.UnitTests.Extensions +{ + public class TypeExtensionsShould + { + [Fact] + public void ConvertToDbType() + { + typeof(bool).ToDbType().Should().Be(DbType.Boolean); + typeof(bool?).ToDbType().Should().Be(DbType.Boolean); + typeof(byte[]).ToDbType().Should().Be(DbType.Binary); + typeof(byte).ToDbType().Should().Be(DbType.Byte); + typeof(byte?).ToDbType().Should().Be(DbType.Byte); + typeof(sbyte).ToDbType().Should().Be(DbType.SByte); + typeof(sbyte?).ToDbType().Should().Be(DbType.SByte); + typeof(short).ToDbType().Should().Be(DbType.Int16); + typeof(short?).ToDbType().Should().Be(DbType.Int16); + typeof(ushort).ToDbType().Should().Be(DbType.UInt16); + typeof(ushort?).ToDbType().Should().Be(DbType.UInt16); + typeof(int).ToDbType().Should().Be(DbType.Int32); + typeof(int?).ToDbType().Should().Be(DbType.Int32); + typeof(uint).ToDbType().Should().Be(DbType.UInt32); + typeof(uint?).ToDbType().Should().Be(DbType.UInt32); + typeof(long).ToDbType().Should().Be(DbType.Int64); + typeof(long?).ToDbType().Should().Be(DbType.Int64); + typeof(ulong).ToDbType().Should().Be(DbType.UInt64); + typeof(ulong?).ToDbType().Should().Be(DbType.UInt64); + typeof(float).ToDbType().Should().Be(DbType.Single); + typeof(float?).ToDbType().Should().Be(DbType.Single); + typeof(decimal).ToDbType().Should().Be(DbType.Decimal); + typeof(decimal?).ToDbType().Should().Be(DbType.Decimal); + typeof(double).ToDbType().Should().Be(DbType.Double); + typeof(double?).ToDbType().Should().Be(DbType.Double); + typeof(DateTime).ToDbType().Should().Be(DbType.DateTime); + typeof(DateTime?).ToDbType().Should().Be(DbType.DateTime); + typeof(Guid).ToDbType().Should().Be(DbType.Guid); + typeof(object).ToDbType().Should().Be(DbType.Binary); + typeof(string).ToDbType().Should().Be(DbType.String); + } + + [Fact] + public void ConvertToSqlDataType() + { + typeof(bool).ToSqlDataType().Should().Be("bit"); + typeof(bool?).ToSqlDataType().Should().Be("bit"); + typeof(byte[]).ToSqlDataType().Should().Be("varBinary"); + typeof(byte).ToSqlDataType().Should().Be("tinyInt"); + typeof(byte?).ToSqlDataType().Should().Be("tinyInt"); + typeof(sbyte).ToSqlDataType().Should().Be("tinyInt"); + typeof(sbyte?).ToSqlDataType().Should().Be("tinyInt"); + typeof(short).ToSqlDataType().Should().Be("smallInt"); + typeof(short?).ToSqlDataType().Should().Be("smallInt"); + typeof(ushort).ToSqlDataType().Should().Be("smallInt"); + typeof(ushort?).ToSqlDataType().Should().Be("smallInt"); + typeof(int).ToSqlDataType().Should().Be("int"); + typeof(int?).ToSqlDataType().Should().Be("int"); + typeof(uint).ToSqlDataType().Should().Be("int"); + typeof(uint?).ToSqlDataType().Should().Be("int"); + typeof(long).ToSqlDataType().Should().Be("bigInt"); + typeof(long?).ToSqlDataType().Should().Be("bigInt"); + typeof(ulong).ToSqlDataType().Should().Be("bigInt"); + typeof(ulong?).ToSqlDataType().Should().Be("bigInt"); + typeof(float).ToSqlDataType().Should().Be("real"); + typeof(float?).ToSqlDataType().Should().Be("real"); + typeof(decimal).ToSqlDataType().Should().Be("decimal"); + typeof(decimal?).ToSqlDataType().Should().Be("decimal"); + typeof(double).ToSqlDataType().Should().Be("float"); + typeof(double?).ToSqlDataType().Should().Be("float"); + typeof(DateTime).ToSqlDataType().Should().Be("dateTime"); + typeof(DateTime?).ToSqlDataType().Should().Be("dateTime"); + typeof(Guid).ToSqlDataType().Should().Be("uniqueIdentifier"); + typeof(object).ToSqlDataType().Should().Be("varBinary"); + typeof(string).ToSqlDataType().Should().Be("nVarChar(255)"); + } + } +} diff --git a/tests/Kros.KORM.UnitTests/Integration/IDatabaseExtensionsShould.cs b/tests/Kros.KORM.UnitTests/Integration/IDatabaseExtensionsShould.cs index 954dba6..1e1ca06 100644 --- a/tests/Kros.KORM.UnitTests/Integration/IDatabaseExtensionsShould.cs +++ b/tests/Kros.KORM.UnitTests/Integration/IDatabaseExtensionsShould.cs @@ -8,7 +8,7 @@ namespace Kros.KORM.UnitTests.Integration { - public class IDatabaseExtensionsShould : DatabaseTestBase + public partial class IDatabaseExtensionsShould : DatabaseTestBase { #region Nested Classes diff --git a/tests/Kros.KORM.UnitTests/Integration/IDatabaseExtensionsTempTableShould.cs b/tests/Kros.KORM.UnitTests/Integration/IDatabaseExtensionsTempTableShould.cs new file mode 100644 index 0000000..51b6056 --- /dev/null +++ b/tests/Kros.KORM.UnitTests/Integration/IDatabaseExtensionsTempTableShould.cs @@ -0,0 +1,179 @@ +using FluentAssertions; +using Kros.Extensions; +using Kros.KORM.UnitTests.Base; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Kros.KORM.UnitTests.Integration +{ + public partial class IDatabaseExtensionsShould : DatabaseTestBase + { + private static readonly string InsertDataScript2 = + $@"INSERT INTO {Table_TestTable} VALUES (1, 18, 'John', 'Smith'); + INSERT INTO {Table_TestTable} VALUES (2, 22, 'Kilie', 'Bistrol'); + INSERT INTO {Table_TestTable} VALUES (3, 77, 'Adam', 'Pribela'); + INSERT INTO {Table_TestTable} VALUES (4, 66, 'Jardo', 'Hornak'); + INSERT INTO {Table_TestTable} VALUES (5, 2, 'Marian', 'Matula'); + INSERT INTO {Table_TestTable} VALUES (6, 122, 'Michal', 'Matis'); + INSERT INTO {Table_TestTable} VALUES (7, 212, 'Peter', 'Kadasi'); + INSERT INTO {Table_TestTable} VALUES (8, 272, 'Aurel', 'Macak'); + INSERT INTO {Table_TestTable} VALUES (9, 227, 'Zuzka', 'Revakova'); + INSERT INTO {Table_TestTable} VALUES (10, 242, 'Andrej', 'Hlava'); + INSERT INTO {Table_TestTable} VALUES (11, 122, 'Johny', 'Slivka');"; + + [Fact] + public void ExecuteWithTempTableList() + { + using (IDatabase database = CreateDatabase(CreateTable_TestTable, InsertDataScript2)) + { + var ids = new List() { 1, 2, 3, 4, 456, 789 }; + var affectedCount = database.ExecuteWithTempTable( + ids, + (database, tableName) => database.ExecuteNonQuery( + $@"UPDATE P + SET P.Age = 18 + FROM People AS P INNER JOIN {tableName} AS T ON (P.Id = T.Value)")); + + affectedCount.Should().Be(4); + } + } + + [Fact] + public async Task ExecuteWithTempTableListAsync() + { + using (IDatabase database = CreateDatabase(CreateTable_TestTable, InsertDataScript2)) + { + var ids = new List() { 1, 2, 3, 4, 456, 789 }; + var affectedCount = await database.ExecuteWithTempTableAsync( + ids, + (database, tableName) => database.ExecuteNonQueryAsync( + $@"UPDATE P + SET P.Age = 18 + FROM People AS P INNER JOIN {tableName} AS T ON (P.Id = T.Value)")); + + affectedCount.Should().Be(4); + } + } + + [Fact] + public void ExecuteWithTempTableTList() + { + using (IDatabase database = CreateDatabase(CreateTable_TestTable, InsertDataScript2)) + { + var ids = new List() { 1, 2, 3, 4, 45 }; + List result = database.ExecuteWithTempTable( + ids, + (database, tableName) => database.Query() + .From($"People AS P INNER JOIN {tableName} AS T ON (P.Id = T.Value)") + .ToList()); + result.Should().HaveCount(4); + } + } + + [Fact] + public async Task ExecuteWithTempTableTListAsync() + { + using (IDatabase database = CreateDatabase(CreateTable_TestTable, InsertDataScript2)) + { + var ids = new List() { 1, 2, 3, 4, 45 }; + IEnumerable result = await database.ExecuteWithTempTableAsync( + ids, + (database, tableName) => database.Query() + .From($"People AS P INNER JOIN {tableName} AS T ON (P.Id = T.Value)") + .ToList() + .AsTask()); + + result.Should().HaveCount(4); + } + } + + [Fact] + public void ExecuteWithTempTableDictionary() + { + using (IDatabase database = CreateDatabase(CreateTable_TestTable, InsertDataScript2)) + { + var names = new Dictionary() { { 1, "jedna" }, { 2, "dva" }, { 12, "dvanast" } }; + + var affectedCount = database.ExecuteWithTempTable( + names, + (database, tableName) => database.ExecuteNonQuery( + @$"UPDATE P + SET P.FirstName = T.Value + FROM People AS P INNER JOIN {tableName} AS T ON (P.Id = T.[Key])")); + + affectedCount.Should().Be(2); + } + } + + [Fact] + public async Task ExecuteWithTempTableDictionaryAsync() + { + using (IDatabase database = CreateDatabase(CreateTable_TestTable, InsertDataScript2)) + { + var names = new Dictionary() { { 1, "jedna" }, { 2, "dva" }, { 12, "dvanast" } }; + + var affectedCount = await database.ExecuteWithTempTableAsync( + names, + (database, tableName) => database.ExecuteNonQueryAsync( + @$"UPDATE P + SET P.FirstName = T.Value + FROM People AS P INNER JOIN {tableName} AS T ON (P.Id = T.[Key])")); + + affectedCount.Should().Be(2); + } + } + + [Fact] + public void ExecuteWithTempTableTDictionary() + { + using (IDatabase database = CreateDatabase(CreateTable_TestTable, InsertDataScript2)) + { + var names = new Dictionary() { { 1, "jedna" }, { 2, "dva" }, { 12, "dvanast" } }; + + List result = database.ExecuteWithTempTable( + names, + (database, tableName) => + { + database.ExecuteNonQuery( + @$"UPDATE P + SET P.FirstName = T.Value + FROM People AS P INNER JOIN {tableName} AS T ON (P.Id = T.[Key])"); + + return database.Query() + .From($"People AS P INNER JOIN {tableName} AS T ON (P.Id = T.[Key])") + .ToList(); + }); + + result.Should().HaveCount(2); + } + } + + [Fact] + public async Task ExecuteWithTempTableTDictionaryAsync() + { + using (IDatabase database = CreateDatabase(CreateTable_TestTable, InsertDataScript2)) + { + var names = new Dictionary() { { 1, "jedna" }, { 2, "dva" }, { 12, "dvanast" } }; + + IEnumerable result = await database.ExecuteWithTempTableAsync( + names, + async (database, tableName) => + { + await database.ExecuteNonQueryAsync( + @$"UPDATE P + SET P.FirstName = T.Value + FROM People AS P INNER JOIN {tableName} AS T ON (P.Id = T.[Key])"); + + return await database.Query() + .From($"People AS P INNER JOIN {tableName} AS T ON (P.Id = T.[Key])") + .ToList() + .AsTask(); + }); + + result.Should().HaveCount(2); + } + } + } +}