diff --git a/.github/workflows/publish-nuget-Package.yml b/.github/workflows/publish-nuget-Package.yml index 46f5a75..fbf7cab 100644 --- a/.github/workflows/publish-nuget-Package.yml +++ b/.github/workflows/publish-nuget-Package.yml @@ -1,7 +1,7 @@ name: Publish Nuget Package env: - NUGET_PACKAGE_NAME_VERSION: "QueryDB.1.1.0.nupkg" + NUGET_PACKAGE_NAME_VERSION: "QueryDB.1.2.0.nupkg" on: workflow_dispatch: diff --git a/CHANGELOG.md b/CHANGELOG.md index 315d9d8..17bdde9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,23 @@ All notable changes to this project documented here. ## [Released] +## [1.2.0](https://www.nuget.org/packages/QueryDB/1.2.0) - 2025-03-04 +### Added +- Asynchronous operations + - `FetchDataAsync()` + - `ExecuteScalarAsync()` + - `ExecuteCommandAsync()` + - `ExecuteTransactionAsync()` +### Changed +- Execute transaction to return transaction outcome and exception details in case of failure instead of logging into console. + +## [1.1.0](https://www.nuget.org/packages/QueryDB/1.1.0) - 2025-02-20 +### Added +- Execute scalar queries (returning a single value). + ## [1.0.0](https://www.nuget.org/packages/QueryDB/1.0.0) - 2025-02-18 ### Added - QueryDB framework for simplified querying and executing transactions across multiple database systems. - Retrieve data from database. - Execute database commands. - - Execute transactions while maintaining atomicity. - -## [1.1.0](https://www.nuget.org/packages/QueryDB/1.1.0) - 2025-02-20 -### Added -- Execute scalar queries (returning a single value). \ No newline at end of file + - Execute transactions while maintaining atomicity. \ No newline at end of file diff --git a/QueryDB.Core.Tests/MSSQLTests.cs b/QueryDB.Core.Tests/MSSQLTests.cs index 5df4bd7..7a569fd 100644 --- a/QueryDB.Core.Tests/MSSQLTests.cs +++ b/QueryDB.Core.Tests/MSSQLTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace QueryDB.Core.Tests { @@ -174,7 +175,150 @@ public void Test_MSSQL_FetchData_Dictionary_DataTypes_Check() #endregion - #region Fetch Data Tests - << List FetchData(string selectSql) >> + #region Fetch Data Async Tests - << Task> FetchDataAsync(string selectSql, bool upperCaseKeys = false) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Dictionary_SelectQuery() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["Agent_Name"] == "Benjamin"); + Assert.AreEqual("A009", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("Benjamin", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Hampshair", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.11", agent.ReferenceData["Commission"]); + Assert.AreEqual("008-22536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Dictionary_SelectQuery_UpperCaseKeys() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_NAME"] == "Benjamin"); + Assert.AreEqual("A009", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Benjamin", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Hampshair", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.11", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("008-22536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Dictionary_SelectQuery_Joins() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["Agent_Code"] == "A004" && X.ReferenceData["Cust_Code"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("Ivan", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("C00006", agent.ReferenceData["Cust_Code"]); + Assert.AreEqual("Shilton", agent.ReferenceData["Cust_Name"]); + Assert.AreEqual("200104", agent.ReferenceData["Ord_Num"]); + Assert.AreEqual("1500.00", agent.ReferenceData["Ord_Amount"]); + Assert.AreEqual("500.00", agent.ReferenceData["Advance_Amount"]); + Assert.AreEqual("SOD", agent.ReferenceData["Ord_Description"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Dictionary_SelectQuery_Joins_UpperCaseKeys() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_CODE"] == "A004" && X.ReferenceData["CUST_CODE"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Ivan", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("C00006", agent.ReferenceData["CUST_CODE"]); + Assert.AreEqual("Shilton", agent.ReferenceData["CUST_NAME"]); + Assert.AreEqual("200104", agent.ReferenceData["ORD_NUM"]); + Assert.AreEqual("1500.00", agent.ReferenceData["ORD_AMOUNT"]); + Assert.AreEqual("500.00", agent.ReferenceData["ADVANCE_AMOUNT"]); + Assert.AreEqual("SOD", agent.ReferenceData["ORD_DESCRIPTION"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Dictionary_SelectQuery_Aliases() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["Agent_Code"] == "A004" && X.ReferenceData["Cust_Code"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("Ivan", agent.ReferenceData["Agent"]); + Assert.AreEqual("Torento", agent.ReferenceData["Agent_Location"]); + Assert.AreEqual("C00006", agent.ReferenceData["Cust_Code"]); + Assert.AreEqual("Shilton", agent.ReferenceData["Customer"]); + Assert.AreEqual("Torento", agent.ReferenceData["Customer_Location"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Dictionary_SelectQuery_Aliases_UpperCaseKeys() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_CODE"] == "A004" && X.ReferenceData["CUST_CODE"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Ivan", agent.ReferenceData["AGENT"]); + Assert.AreEqual("Torento", agent.ReferenceData["AGENT_LOCATION"]); + Assert.AreEqual("C00006", agent.ReferenceData["CUST_CODE"]); + Assert.AreEqual("Shilton", agent.ReferenceData["CUSTOMER"]); + Assert.AreEqual("Torento", agent.ReferenceData["CUSTOMER_LOCATION"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Dictionary_DataTypes_Check() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql_DataTypes; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(1, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual("9223372036854775807", dataType.ReferenceData["BigInt_Column"]); + Assert.AreEqual("EjRWeJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", dataType.ReferenceData["Binary_Column"]); + Assert.AreEqual("True", dataType.ReferenceData["Bit_Column"]); + Assert.AreEqual("CharData", dataType.ReferenceData["Char_Column"]); + Assert.AreEqual("09/21/2024 00:00:00", ConvertToUSFormat(dataType.ReferenceData["Date_Column"])); + Assert.AreEqual("09/21/2024 08:34:51", ConvertToUSFormat(dataType.ReferenceData["DateTime_Column"])); + Assert.AreEqual("09/21/2024 08:34:51", ConvertToUSFormat(dataType.ReferenceData["DateTime2_Column"])); + Assert.AreEqual("09/20/2024 22:34:51", ConvertToUTCInUSFormat(dataType.ReferenceData["DateTimeOffset_Column"])); + Assert.AreEqual("123456.78", dataType.ReferenceData["Decimal_Column"]); + Assert.AreEqual("123456.78", dataType.ReferenceData["Float_Column"]); + Assert.AreEqual("EjRWeJA=", dataType.ReferenceData["Image_Column"]); + Assert.AreEqual("2147483647", dataType.ReferenceData["Int_Column"]); + Assert.AreEqual("123456.7800", dataType.ReferenceData["Money_Column"]); + Assert.AreEqual("NCharData", dataType.ReferenceData["NChar_Column"]); + Assert.AreEqual("NTextData", dataType.ReferenceData["NText_Column"]); + Assert.AreEqual("123456.78", dataType.ReferenceData["Numeric_Column"]); + Assert.AreEqual("NVarCharData", dataType.ReferenceData["NVarChar_Column"]); + Assert.AreEqual("123.45", dataType.ReferenceData["Real_Column"]); + Assert.AreEqual("09/21/2024 08:35:00", ConvertToUSFormat(dataType.ReferenceData["SmallDateTime_Column"])); + Assert.AreEqual("32767", dataType.ReferenceData["SmallInt_Column"]); + Assert.AreEqual("123456.7800", dataType.ReferenceData["SmallMoney_Column"]); + Assert.AreEqual("SampleVariant", dataType.ReferenceData["SqlVariant_Column"]); + Assert.AreEqual("TextData", dataType.ReferenceData["Text_Column"]); + Assert.AreEqual("08:34:51", dataType.ReferenceData["Time_Column"]); + Assert.AreEqual("255", dataType.ReferenceData["TinyInt_Column"]); + Assert.AreEqual("12345678-1234-1234-1234-123456789012", dataType.ReferenceData["UniqueIdentifier_Column"]); + Assert.AreEqual("EjRWeJA=", dataType.ReferenceData["VarBinary_Column"]); + Assert.AreEqual("VarCharData", dataType.ReferenceData["VarChar_Column"]); + Assert.AreEqual("XmlData", dataType.ReferenceData["Xml_Column"]); + } + + #endregion + + #region Fetch Data Tests - << List FetchData(string selectSql, bool strict = false) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] @@ -301,6 +445,7 @@ public void Test_MSSQL_FetchData_Entity_Strict_Error_Check() try { var data = new DBContext(DB.MSSQL, MSSQLConnectionString).FetchData(selectSql, strict: true); + Assert.Fail("No Exception"); } catch (IndexOutOfRangeException ex) { @@ -310,7 +455,144 @@ public void Test_MSSQL_FetchData_Entity_Strict_Error_Check() #endregion - #region Execute Scalar Tests - << string ExecuteScalar(string sqlStatement); >> + #region Fetch Data Async Tests - << Task> FetchDataAsync(string selectSql, bool strict = false) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Entity_SelectQuery() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Name == "Benjamin"); + Assert.AreEqual("A009", agent.Agent_Code); + Assert.AreEqual("Benjamin", agent.Agent_Name); + Assert.AreEqual("Hampshair", agent.Working_Area); + Assert.AreEqual((decimal)0.11, agent.Commission); + Assert.AreEqual("008-22536178", agent.Phone_No); + Assert.AreEqual("", agent.Country); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Entity_SelectQuery_Joins() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Code == "A004" && X.Cust_Code == "C00006"); + Assert.AreEqual("A004", agent.Agent_Code); + Assert.AreEqual("Ivan", agent.Agent_Name); + Assert.AreEqual("C00006", agent.Cust_Code); + Assert.AreEqual("Shilton", agent.Cust_Name); + Assert.AreEqual(200104, agent.Ord_Num); + Assert.AreEqual((decimal)1500.00, agent.Ord_Amount); + Assert.AreEqual((decimal)500.00, agent.Advance_Amount); + Assert.AreEqual("SOD", agent.Ord_Description); + // Non Existent Query Data + Assert.IsNull(agent.Agent); + Assert.IsNull(agent.Agent_Location); + Assert.IsNull(agent.Customer); + Assert.IsNull(agent.Customer_Location); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Entity_SelectQuery_Aliases() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Code == "A004" && X.Cust_Code == "C00006"); + Assert.AreEqual("A004", agent.Agent_Code); + Assert.AreEqual("Ivan", agent.Agent); + Assert.AreEqual("Torento", agent.Agent_Location); + Assert.AreEqual("C00006", agent.Cust_Code); + Assert.AreEqual("Shilton", agent.Customer); + Assert.AreEqual("Torento", agent.Customer_Location); + // Non Existent Query Data + Assert.IsNull(agent.Agent_Name); + Assert.IsNull(agent.Cust_Name); + Assert.AreEqual(0, agent.Ord_Num); + Assert.AreEqual(0, agent.Ord_Amount); + Assert.AreEqual(0, agent.Advance_Amount); + Assert.IsNull(agent.Ord_Description); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Entity_DataTypes_Check() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql_DataTypes; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(1, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual(9223372036854775807, dataType.BigInt_Column); + Assert.AreEqual("EjRWeJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", ConvertByteArrayToBase64(dataType.Binary_Column)); + Assert.IsTrue(dataType.Bit_Column); + Assert.AreEqual("CharData", dataType.Char_Column); + Assert.AreEqual("09/21/2024 00:00:00", ConvertToUSFormat(dataType.Date_Column.ToString())); + Assert.AreEqual("09/21/2024 08:34:51", ConvertToUSFormat(dataType.DateTime_Column.ToString())); + Assert.AreEqual("09/21/2024 08:34:51", ConvertToUSFormat(dataType.DateTime2_Column.ToString())); + Assert.AreEqual("09/20/2024 22:34:51", ConvertToUTCInUSFormat(dataType.DateTimeOffset_Column.ToString())); + Assert.AreEqual((decimal)123456.78, dataType.Decimal_Column); + Assert.AreEqual((double)123456.78, dataType.Float_Column); + Assert.AreEqual("EjRWeJA=", ConvertByteArrayToBase64(dataType.Image_Column)); + Assert.AreEqual(2147483647, dataType.Int_Column); + Assert.AreEqual((decimal)123456.7800, dataType.Money_Column); + Assert.AreEqual("NCharData", dataType.NChar_Column); + Assert.AreEqual("NTextData", dataType.NText_Column); + Assert.AreEqual((decimal)123456.78, dataType.Numeric_Column); + Assert.AreEqual("NVarCharData", dataType.NVarChar_Column); + Assert.AreEqual((float)123.45, dataType.Real_Column); + Assert.AreEqual("09/21/2024 08:35:00", ConvertToUSFormat(dataType.SmallDateTime_Column.ToString())); + Assert.AreEqual(32767, dataType.SmallInt_Column); + Assert.AreEqual((decimal)123456.7800, dataType.SmallMoney_Column); + Assert.AreEqual("SampleVariant", dataType.SqlVariant_Column.ToString()); + Assert.AreEqual("TextData", dataType.Text_Column); + Assert.AreEqual("08:34:51", dataType.Time_Column.ToString()); + Assert.AreEqual(255, dataType.TinyInt_Column); + Assert.AreEqual("12345678-1234-1234-1234-123456789012", dataType.UniqueIdentifier_Column.ToString()); + Assert.AreEqual("EjRWeJA=", ConvertByteArrayToBase64(dataType.VarBinary_Column)); + Assert.AreEqual("VarCharData", dataType.VarChar_Column); + Assert.AreEqual("XmlData", dataType.Xml_Column); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Entity_Strict_Check() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql_Strict; + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql, strict: true); + Assert.AreEqual(34, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual("A003", dataType.Agent_Code); + Assert.AreEqual("Alex", dataType.Agent); + Assert.AreEqual("C00013", dataType.Cust_Code); + Assert.AreEqual("Holmes", dataType.Customer); + Assert.AreEqual(200100, dataType.Ord_Num); + Assert.AreEqual((decimal)1000.00, dataType.Ord_Amount); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_FetchDataAsync_Entity_Strict_Error_Check() + { + var selectSql = Queries.MSSQLQueries.TestDB.SelectSql_Strict; + try + { + var data = await new DBContext(DB.MSSQL, MSSQLConnectionString).FetchDataAsync(selectSql, strict: true); + Assert.Fail("No Exception"); + } + catch (IndexOutOfRangeException ex) + { + Assert.AreEqual("Agent_Name", ex.Message); + } + } + + #endregion + + #region Execute Scalar Tests - << string ExecuteScalar(string sqlStatement) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] @@ -396,7 +678,93 @@ public void Test_MSSQL_ExecuteScalar_As_StringReturn_UnsupportedCommands() #endregion - #region Execute Scalar Tests - << T ExecuteScalar(string sqlStatement); >> + #region Execute Scalar Async Tests - << Task ExecuteScalarAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteScalarAsync_As_StringReturn_Scalar_Queries() + { + var countOfRecords = Queries.MSSQLQueries.TestDB.ScalarQueries.Count_Of_Records; + var max = Queries.MSSQLQueries.TestDB.ScalarQueries.Max; + var min = Queries.MSSQLQueries.TestDB.ScalarQueries.Min; + var sum = Queries.MSSQLQueries.TestDB.ScalarQueries.Sum; + var avg = Queries.MSSQLQueries.TestDB.ScalarQueries.Avg; + var singleValueSelect = Queries.MSSQLQueries.TestDB.ScalarQueries.Single_Value_Select; + + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + var count = await dbContext.ExecuteScalarAsync(countOfRecords); + Assert.AreEqual("12", count); + var maxValue = await dbContext.ExecuteScalarAsync(max); + Assert.AreEqual("10000.00", maxValue); + var minValue = await dbContext.ExecuteScalarAsync(min); + Assert.AreEqual("3000.00", minValue); + var sumValue = await dbContext.ExecuteScalarAsync(sum); + Assert.AreEqual("161000.00", sumValue); + var avgValue = await dbContext.ExecuteScalarAsync(avg); + Assert.AreEqual("6520.000000", avgValue); + var singleValue = await dbContext.ExecuteScalarAsync(singleValueSelect); + Assert.AreEqual("2", singleValue); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteScalarAsync_As_StringReturn_DefaultValue() + { + var noValueReturned = Queries.MSSQLQueries.TestDB.ScalarQueries.No_Value_Returned; + var dBNullValue = Queries.MSSQLQueries.TestDB.ScalarQueries.DB_Null_Value; + + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + var result = await dbContext.ExecuteScalarAsync(noValueReturned); + Assert.IsInstanceOfType(result); + Assert.AreEqual("", result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(string.Empty, result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteScalarAsync_As_StringReturn_UnsupportedCommands() + { + var sqlStatements = new List + { + Queries.MSSQLQueries.TestDB.DDL.Create_Table, + Queries.MSSQLQueries.TestDB.DDL.Alter_Table, + Queries.MSSQLQueries.TestDB.DDL.Comment_Table, + Queries.MSSQLQueries.TestDB.DDL.Truncate_Table, + Queries.MSSQLQueries.TestDB.DDL.Drop_Table, + + Queries.MSSQLQueries.TestDB.DML.InsertSql, + Queries.MSSQLQueries.TestDB.DML.UpdateSql, + Queries.MSSQLQueries.TestDB.DML.DeleteSql, + + Queries.MSSQLQueries.TestDB.DCL.GrantSql_Command_Table_User, + Queries.MSSQLQueries.TestDB.DCL.RevokeSql_Command_Table_User + }; + + foreach (var sqlStatement in sqlStatements) + { + try + { + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + await dbContext.ExecuteScalarAsync(sqlStatement); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("Only SELECT queries are supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteScalar' only supports SELECT queries that have a scalar (single value) return.", ex.AdditionalInfo); + } + } + } + + #endregion + + #region Execute Scalar Tests - << T ExecuteScalar(string sqlStatement) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] @@ -453,78 +821,243 @@ public void Test_MSSQL_ExecuteScalar_As_TypedReturn_DefaultValue() Assert.IsInstanceOfType(result); Assert.AreEqual(default(ulong), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(ushort), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(decimal), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(double), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(float), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(byte), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(bool), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(DateTime), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(Guid), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(string), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(int?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(long?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(short?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(decimal?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(double?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(float?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(bool?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(DateTime?), result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public void Test_MSSQL_ExecuteScalar_As_TypedReturn_UnsupportedCommands() + { + var sqlStatements = new List + { + Queries.MSSQLQueries.TestDB.DDL.Create_Table, + Queries.MSSQLQueries.TestDB.DDL.Alter_Table, + Queries.MSSQLQueries.TestDB.DDL.Comment_Table, + Queries.MSSQLQueries.TestDB.DDL.Truncate_Table, + Queries.MSSQLQueries.TestDB.DDL.Drop_Table, + + Queries.MSSQLQueries.TestDB.DML.InsertSql, + Queries.MSSQLQueries.TestDB.DML.UpdateSql, + Queries.MSSQLQueries.TestDB.DML.DeleteSql, + + Queries.MSSQLQueries.TestDB.DCL.GrantSql_Command_Table_User, + Queries.MSSQLQueries.TestDB.DCL.RevokeSql_Command_Table_User + }; + + foreach (var sqlStatement in sqlStatements) + { + try + { + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + dbContext.ExecuteScalar(sqlStatement); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("Only SELECT queries are supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteScalar' only supports SELECT queries that have a scalar (single value) return.", ex.AdditionalInfo); + } + } + } + + #endregion + + #region Execute Scalar Async Tests - << Task ExecuteScalarAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteScalarAsync_As_TypedReturn_Scalar_Queries() + { + var countOfRecords = Queries.MSSQLQueries.TestDB.ScalarQueries.Count_Of_Records; + var max = Queries.MSSQLQueries.TestDB.ScalarQueries.Max; + var min = Queries.MSSQLQueries.TestDB.ScalarQueries.Min; + var sum = Queries.MSSQLQueries.TestDB.ScalarQueries.Sum; + var avg = Queries.MSSQLQueries.TestDB.ScalarQueries.Avg; + var singleValueSelect = Queries.MSSQLQueries.TestDB.ScalarQueries.Single_Value_Select; + + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + var count = await dbContext.ExecuteScalarAsync(countOfRecords); + Assert.AreEqual(12, count); + var maxValue = await dbContext.ExecuteScalarAsync(max); + Assert.AreEqual(10000.00, maxValue); + var minValue = await dbContext.ExecuteScalarAsync(min); + Assert.AreEqual(3000.00, minValue); + var sumValue = await dbContext.ExecuteScalarAsync(sum); + Assert.AreEqual(161000.00, sumValue); + var avgValue = await dbContext.ExecuteScalarAsync(avg); + Assert.AreEqual((decimal)6520.000000, avgValue); + var singleValue = await dbContext.ExecuteScalarAsync(singleValueSelect); + Assert.AreEqual("2", singleValue); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteScalarAsync_As_TypedReturn_DefaultValue() + { + var dBNullValue = Queries.MSSQLQueries.TestDB.ScalarQueries.DB_Null_Value; + + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + dynamic result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(int), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(long), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(short), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(uint), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(ulong), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(ushort), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(decimal), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(double), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(float), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(byte), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(bool), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(DateTime), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(Guid), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(string), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(int?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(long?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(short?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(decimal?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(double?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(float?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(bool?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(DateTime?), result); } [TestMethod] [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] - public void Test_MSSQL_ExecuteScalar_As_TypedReturn_UnsupportedCommands() + public async Task Test_MSSQL_ExecuteScalarAsync_As_TypedReturn_UnsupportedCommands() { var sqlStatements = new List { @@ -547,7 +1080,7 @@ public void Test_MSSQL_ExecuteScalar_As_TypedReturn_UnsupportedCommands() try { var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); - dbContext.ExecuteScalar(sqlStatement); + await dbContext.ExecuteScalarAsync(sqlStatement); Assert.Fail("No Exception"); } catch (QueryDBException ex) @@ -732,6 +1265,180 @@ public void Test_MSSQL_ExecuteCommand_DCL_Queries() #endregion + #region Execute Command Async Tests - << Task ExecuteCommandAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteCommandAsync_DDL_Queries() + { + var createTableSql = Queries.MSSQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.MSSQLQueries.TestDB.DDL.Alter_Table; + var commentTableSql = Queries.MSSQLQueries.TestDB.DDL.Comment_Table; + var commentTableColumnSql = Queries.MSSQLQueries.TestDB.DDL.Comment_Table_Column; + var truncateTableSql = Queries.MSSQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.MSSQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.MSSQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.MSSQLQueries.TestDB.DDL.DDL_Execute_check; + var dDLTableCommentCheckSql = Queries.MSSQLQueries.TestDB.DDL.DDL_Table_Comment_check; + var dDLTableColumnCommentCheckSql = Queries.MSSQLQueries.TestDB.DDL.DDL_Table_Column_Comment_check; + + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + await dbContext.ExecuteCommandAsync(createTableSql); + await dbContext.ExecuteCommandAsync(alterTableSql); + await dbContext.ExecuteCommandAsync(commentTableSql); + await dbContext.ExecuteCommandAsync(commentTableColumnSql); + await dbContext.ExecuteCommandAsync(truncateTableSql); + + var tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "dbo", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + var tableComment = await dbContext + .FetchDataAsync(string.Format(dDLTableCommentCheckSql, "dbo", "Employee")); + Assert.AreEqual("This table stores employee records", tableComment[0].ReferenceData["Table_Comment"]); + var tableColumnComment = await dbContext + .FetchDataAsync(string.Format(dDLTableColumnCommentCheckSql, "dbo", "Employee")); + Assert.AreEqual("This column stores employee middle name", tableColumnComment[0].ReferenceData["Table_Column_Comment"]); + + await dbContext.ExecuteCommandAsync(renameTableSql); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "dbo", "Employee")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "dbo", "Employees")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + + await dbContext.ExecuteCommandAsync(dropTableSql); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "dbo", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteCommandAsync_DML_Queries() + { + var insertSql = Queries.MSSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MSSQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.MSSQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.MSSQLQueries.TestDB.DML.VerifyDMLExecution; + + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + // Insert + var rows = await dbContext.ExecuteCommandAsync(insertSql); + Assert.AreEqual(1, rows); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.11", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Update + rows = await dbContext.ExecuteCommandAsync(updateSql); + Assert.AreEqual(1, rows); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.15", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Delete + rows = await dbContext.ExecuteCommandAsync(deleteSql); + Assert.AreEqual(1, rows); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteCommandAsync_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.MSSQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + var rows = await dbContext.ExecuteCommandAsync(selectSql); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteCommand' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteCommandAsync_DCL_Queries() + { + var login = "test_user"; + var user = "test_user"; + var password = "Test@1234"; + var table = "agents"; + var commands = "SELECT, UPDATE"; + var checkCommand = "SELECT"; + + var createLogin = string.Format(Queries.MSSQLQueries.TestDB.DCL.CreateLoginSql_Login_Password, login, password); + var createUser = string.Format(Queries.MSSQLQueries.TestDB.DCL.CreateUserSql_User_Login, user, login); + var grantSql = string.Format(Queries.MSSQLQueries.TestDB.DCL.GrantSql_Command_Table_User, commands, table, user); + var revokeSql = string.Format(Queries.MSSQLQueries.TestDB.DCL.RevokeSql_Command_Table_User, commands, table, user); + var verifyPermissions = string.Format(Queries.MSSQLQueries.TestDB.DCL.VerifyPermission_User_Table_Command, user, table, checkCommand); + var removeUser = string.Format(Queries.MSSQLQueries.TestDB.DCL.RemoveUserSql_User, user); + var removeLogin = string.Format(Queries.MSSQLQueries.TestDB.DCL.RemoveLoginSql_Login, login); + + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + // Create Login + var result = await dbContext.ExecuteCommandAsync(createLogin); + Assert.AreEqual(-1, result); + + // Create User + result = await dbContext.ExecuteCommandAsync(createUser); + Assert.AreEqual(-1, result); + + // Existing Permissions + var data = await dbContext.FetchDataAsync(verifyPermissions); + var dataRow = data.FirstOrDefault(); + Assert.AreEqual("0", dataRow.ReferenceData["HasPermission"]); + + // Grant + result = await dbContext.ExecuteCommandAsync(grantSql); + Assert.AreEqual(-1, result); + data = await dbContext.FetchDataAsync(verifyPermissions); + dataRow = data.FirstOrDefault(); + Assert.AreEqual("1", dataRow.ReferenceData["HasPermission"]); + + // Revoke + result = await dbContext.ExecuteCommandAsync(revokeSql); + Assert.AreEqual(-1, result); + data = await dbContext.FetchDataAsync(verifyPermissions); + dataRow = data.FirstOrDefault(); + Assert.AreEqual("0", dataRow.ReferenceData["HasPermission"]); + + // Remove User + result = await dbContext.ExecuteCommandAsync(removeUser); + Assert.AreEqual(-1, result); + + // Remove Login + result = await dbContext.ExecuteCommandAsync(removeLogin); + Assert.AreEqual(-1, result); + } + + #endregion + #region Execute Transaction Tests - << bool ExecuteTransaction(List sqlStatements) >> [TestMethod] @@ -754,7 +1461,7 @@ public void Test_MSSQL_ExecuteTransaction_DDL_Multiple_Queries() }; var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); var result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); var tableCount = dbContext .FetchData(string.Format(dDLExecutionCheckSql, "dbo", "Employee")); @@ -767,7 +1474,7 @@ public void Test_MSSQL_ExecuteTransaction_DDL_Multiple_Queries() dropTableSql }; result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); tableCount = dbContext .FetchData(string.Format(dDLExecutionCheckSql, "dbo", "Employees")); @@ -792,7 +1499,7 @@ public void Test_MSSQL_ExecuteTransaction_DML_Multiple_Queries() // Insert & Update var result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); var data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(1, data.Count); var agent = data.FirstOrDefault(); @@ -809,7 +1516,7 @@ public void Test_MSSQL_ExecuteTransaction_DML_Multiple_Queries() deleteSql }; result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(0, data.Count); } @@ -833,7 +1540,8 @@ public void Test_MSSQL_ExecuteTransaction_Incomplete_Transaction_Rollback_On_Err // Insert & Update var result = dbContext.ExecuteTransaction(statements); - Assert.IsFalse(result); + Assert.IsFalse(result.Success); + Assert.AreEqual("Incorrect syntax near 'UPDATE'.", result.Exception.Message); var data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(0, data.Count); } @@ -865,6 +1573,140 @@ public void Test_MSSQL_ExecuteTransaction_DML_Unsupported_SELECT_Queries() #endregion + #region Execute Transaction Async Tests - << Task ExecuteTransactionAsync(List sqlStatements) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteTransactionAsync_DDL_Multiple_Queries() + { + var createTableSql = Queries.MSSQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.MSSQLQueries.TestDB.DDL.Alter_Table; + var truncateTableSql = Queries.MSSQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.MSSQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.MSSQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.MSSQLQueries.TestDB.DDL.DDL_Execute_check; + + // Create, Alter & Truncate + var statements = new List + { + createTableSql, + alterTableSql, + truncateTableSql + }; + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + + var tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "dbo", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + + // Rename & Drop + statements = new List + { + renameTableSql, + dropTableSql + }; + result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "dbo", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteTransactionAsync_DML_Multiple_Queries() + { + var insertSql = Queries.MSSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MSSQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.MSSQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.MSSQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql + }; + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + // Insert & Update + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.15", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Delete + statements = new List + { + deleteSql + }; + result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteTransactionAsync_Incomplete_Transaction_Rollback_On_Error() + { + var insertSql = Queries.MSSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MSSQLQueries.TestDB.DML.UpdateSql; + var updateErrorSql = "UPDATE"; + var verifyDMLExecution = Queries.MSSQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql, + updateErrorSql + }; + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + + // Insert & Update + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsFalse(result.Success); + Assert.AreEqual("Incorrect syntax near 'UPDATE'.", result.Exception.Message); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MSSQL_TESTS)] + public async Task Test_MSSQL_ExecuteTransactionAsync_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.MSSQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var statements = new List + { + selectSql + }; + var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteTransaction' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + #endregion + #endregion } diff --git a/QueryDB.Core.Tests/MySQLTests.cs b/QueryDB.Core.Tests/MySQLTests.cs index ef00ffc..6dd4469 100644 --- a/QueryDB.Core.Tests/MySQLTests.cs +++ b/QueryDB.Core.Tests/MySQLTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace QueryDB.Core.Tests { @@ -164,7 +165,140 @@ public void Test_MySQL_FetchData_Dictionary_DataTypes_Check() #endregion - #region Fetch Data Tests - << List FetchData(string selectSql) >> + #region Fetch Data Async Tests - << Task> FetchDataAsync(string selectSql, bool upperCaseKeys = false) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Dictionary_SelectQuery() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["Agent_Name"] == "Benjamin"); + Assert.AreEqual("A009", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("Benjamin", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Hampshair", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.11", agent.ReferenceData["Commission"]); + Assert.AreEqual("008-22536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Dictionary_SelectQuery_UpperCaseKeys() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_NAME"] == "Benjamin"); + Assert.AreEqual("A009", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Benjamin", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Hampshair", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.11", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("008-22536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Dictionary_SelectQuery_Joins() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["Agent_Code"] == "A004" && X.ReferenceData["Cust_Code"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("Ivan", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("C00006", agent.ReferenceData["Cust_Code"]); + Assert.AreEqual("Shilton", agent.ReferenceData["Cust_Name"]); + Assert.AreEqual("200104", agent.ReferenceData["Ord_Num"]); + Assert.AreEqual("1500.00", agent.ReferenceData["Ord_Amount"]); + Assert.AreEqual("500.00", agent.ReferenceData["Advance_Amount"]); + Assert.AreEqual("SOD", agent.ReferenceData["Ord_Description"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Dictionary_SelectQuery_Joins_UpperCaseKeys() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_CODE"] == "A004" && X.ReferenceData["CUST_CODE"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Ivan", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("C00006", agent.ReferenceData["CUST_CODE"]); + Assert.AreEqual("Shilton", agent.ReferenceData["CUST_NAME"]); + Assert.AreEqual("200104", agent.ReferenceData["ORD_NUM"]); + Assert.AreEqual("1500.00", agent.ReferenceData["ORD_AMOUNT"]); + Assert.AreEqual("500.00", agent.ReferenceData["ADVANCE_AMOUNT"]); + Assert.AreEqual("SOD", agent.ReferenceData["ORD_DESCRIPTION"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Dictionary_SelectQuery_Aliases() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["Agent_Code"] == "A004" && X.ReferenceData["Cust_Code"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("Ivan", agent.ReferenceData["Agent"]); + Assert.AreEqual("Torento", agent.ReferenceData["Agent_Location"]); + Assert.AreEqual("C00006", agent.ReferenceData["Cust_Code"]); + Assert.AreEqual("Shilton", agent.ReferenceData["Customer"]); + Assert.AreEqual("Torento", agent.ReferenceData["Customer_Location"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Dictionary_SelectQuery_Aliases_UpperCaseKeys() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_CODE"] == "A004" && X.ReferenceData["CUST_CODE"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Ivan", agent.ReferenceData["AGENT"]); + Assert.AreEqual("Torento", agent.ReferenceData["AGENT_LOCATION"]); + Assert.AreEqual("C00006", agent.ReferenceData["CUST_CODE"]); + Assert.AreEqual("Shilton", agent.ReferenceData["CUSTOMER"]); + Assert.AreEqual("Torento", agent.ReferenceData["CUSTOMER_LOCATION"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Dictionary_DataTypes_Check() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql_DataTypes; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(1, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual("9223372036854775807", dataType.ReferenceData["BigInt_Column"]); + Assert.AreEqual("1", dataType.ReferenceData["Bit_Column"]); + Assert.AreEqual("A", dataType.ReferenceData["Char_Column"]); + Assert.AreEqual("09/21/2024 00:00:00", ConvertToUSFormat(dataType.ReferenceData["Date_Column"])); + Assert.AreEqual("09/21/2024 13:24:10", ConvertToUSFormat(dataType.ReferenceData["DateTime_Column"])); + Assert.AreEqual("12345.67", dataType.ReferenceData["Decimal_Column"]); + Assert.AreEqual("123.45", dataType.ReferenceData["Float_Column"]); + Assert.AreEqual("2147483647", dataType.ReferenceData["Int_Column"]); + Assert.AreEqual("This is a long text", dataType.ReferenceData["LongText_Column"]); + Assert.AreEqual("8388607", dataType.ReferenceData["MediumInt_Column"]); + Assert.AreEqual("This is a medium text", dataType.ReferenceData["MediumText_Column"]); + Assert.AreEqual("32767", dataType.ReferenceData["SmallInt_Column"]); + Assert.AreEqual("This is a text", dataType.ReferenceData["Text_Column"]); + Assert.AreEqual("13:24:10", dataType.ReferenceData["Time_Column"]); + Assert.AreEqual("09/21/2024 13:24:10", ConvertToUSFormat(dataType.ReferenceData["Timestamp_Column"])); + Assert.AreEqual("127", dataType.ReferenceData["TinyInt_Column"]); + Assert.AreEqual("This is a tiny text", dataType.ReferenceData["TinyText_Column"]); + Assert.AreEqual("3q2+7w==", dataType.ReferenceData["VarBinary_Column"]); + Assert.AreEqual("This is a varchar", dataType.ReferenceData["VarChar_Column"]); + } + + #endregion + + #region Fetch Data Tests - << List FetchData(string selectSql, bool strict = false) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] @@ -281,6 +415,134 @@ public void Test_MySQL_FetchData_Entity_Strict_Error_Check() try { var data = new DBContext(DB.MySQL, MySQLConnectionString).FetchData(selectSql, strict: true); + Assert.Fail("No Exception"); + } + catch (IndexOutOfRangeException ex) + { + Assert.AreEqual("Could not find specified column in results: Agent_Name", ex.Message); + } + } + + #endregion + + #region Fetch Data Async Tests - << List FetchData(string selectSql, bool strict = false) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Entity_SelectQuery() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Name == "Benjamin"); + Assert.AreEqual("A009", agent.Agent_Code); + Assert.AreEqual("Benjamin", agent.Agent_Name); + Assert.AreEqual("Hampshair", agent.Working_Area); + Assert.AreEqual((decimal)0.11, agent.Commission); + Assert.AreEqual("008-22536178", agent.Phone_No); + Assert.AreEqual("", agent.Country); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Entity_SelectQuery_Joins() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Code == "A004" && X.Cust_Code == "C00006"); + Assert.AreEqual("A004", agent.Agent_Code); + Assert.AreEqual("Ivan", agent.Agent_Name); + Assert.AreEqual("C00006", agent.Cust_Code); + Assert.AreEqual("Shilton", agent.Cust_Name); + Assert.AreEqual(200104, agent.Ord_Num); + Assert.AreEqual((decimal)1500.00, agent.Ord_Amount); + Assert.AreEqual((decimal)500.00, agent.Advance_Amount); + Assert.AreEqual("SOD", agent.Ord_Description); + // Non Existent Query Data + Assert.IsNull(agent.Agent); + Assert.IsNull(agent.Agent_Location); + Assert.IsNull(agent.Customer); + Assert.IsNull(agent.Customer_Location); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Entity_SelectQuery_Aliases() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Code == "A004" && X.Cust_Code == "C00006"); + Assert.AreEqual("A004", agent.Agent_Code); + Assert.AreEqual("Ivan", agent.Agent); + Assert.AreEqual("Torento", agent.Agent_Location); + Assert.AreEqual("C00006", agent.Cust_Code); + Assert.AreEqual("Shilton", agent.Customer); + Assert.AreEqual("Torento", agent.Customer_Location); + // Non Existent Query Data + Assert.IsNull(agent.Agent_Name); + Assert.IsNull(agent.Cust_Name); + Assert.AreEqual(0, agent.Ord_Num); + Assert.AreEqual(0, agent.Ord_Amount); + Assert.AreEqual(0, agent.Advance_Amount); + Assert.IsNull(agent.Ord_Description); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Entity_DataTypes_Check() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql_DataTypes; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(1, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual(9223372036854775807, dataType.BigInt_Column); + Assert.AreEqual((ulong?)1, dataType.Bit_Column); + Assert.AreEqual("A", dataType.Char_Column); + Assert.AreEqual("09/21/2024 00:00:00", ConvertToUSFormat(dataType.Date_Column.ToString())); + Assert.AreEqual("09/21/2024 13:24:10", ConvertToUSFormat(dataType.DateTime_Column.ToString())); + Assert.AreEqual((decimal)12345.67, dataType.Decimal_Column); + Assert.AreEqual((float)123.45, dataType.Float_Column); + Assert.AreEqual(2147483647, dataType.Int_Column); + Assert.AreEqual("This is a long text", dataType.LongText_Column); + Assert.AreEqual(8388607, dataType.MediumInt_Column); + Assert.AreEqual("This is a medium text", dataType.MediumText_Column); + Assert.AreEqual((short)32767, dataType.SmallInt_Column); + Assert.AreEqual("This is a text", dataType.Text_Column); + Assert.AreEqual("13:24:10", dataType.Time_Column.ToString()); + Assert.AreEqual("09/21/2024 13:24:10", ConvertToUSFormat(dataType.Timestamp_Column.ToString())); + Assert.AreEqual((sbyte?)127, dataType.TinyInt_Column); + Assert.AreEqual("This is a tiny text", dataType.TinyText_Column); + Assert.AreEqual("3q2+7w==", ConvertByteArrayToBase64(dataType.VarBinary_Column)); + Assert.AreEqual("This is a varchar", dataType.VarChar_Column); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Entity_Strict_Check() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql_Strict; + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql, strict: true); + Assert.AreEqual(34, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual("A003", dataType.Agent_Code); + Assert.AreEqual("Alex", dataType.Agent); + Assert.AreEqual("C00013", dataType.Cust_Code); + Assert.AreEqual("Holmes", dataType.Customer); + Assert.AreEqual(200100, dataType.Ord_Num); + Assert.AreEqual((decimal)1000.00, dataType.Ord_Amount); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_FetchDataAsync_Entity_Strict_Error_Check() + { + var selectSql = Queries.MySQLQueries.TestDB.SelectSql_Strict; + try + { + var data = await new DBContext(DB.MySQL, MySQLConnectionString).FetchDataAsync(selectSql, strict: true); + Assert.Fail("No Exception"); } catch (IndexOutOfRangeException ex) { @@ -290,7 +552,7 @@ public void Test_MySQL_FetchData_Entity_Strict_Error_Check() #endregion - #region Execute Scalar Tests - << string ExecuteScalar(string sqlStatement); >> + #region Execute Scalar Tests - << string ExecuteScalar(string sqlStatement) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] @@ -376,7 +638,93 @@ public void Test_MySQL_ExecuteScalar_As_StringReturn_UnsupportedCommands() #endregion - #region Execute Scalar Tests - << T ExecuteScalar(string sqlStatement); >> + #region Execute Scalar Async Tests - << Task ExecuteScalarAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteScalarAsync_As_StringReturn_Scalar_Queries() + { + var countOfRecords = Queries.MySQLQueries.TestDB.ScalarQueries.Count_Of_Records; + var max = Queries.MySQLQueries.TestDB.ScalarQueries.Max; + var min = Queries.MySQLQueries.TestDB.ScalarQueries.Min; + var sum = Queries.MySQLQueries.TestDB.ScalarQueries.Sum; + var avg = Queries.MySQLQueries.TestDB.ScalarQueries.Avg; + var singleValueSelect = Queries.MySQLQueries.TestDB.ScalarQueries.Single_Value_Select; + + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + var count = await dbContext.ExecuteScalarAsync(countOfRecords); + Assert.AreEqual("12", count); + var maxValue = await dbContext.ExecuteScalarAsync(max); + Assert.AreEqual("10000.00", maxValue); + var minValue = await dbContext.ExecuteScalarAsync(min); + Assert.AreEqual("3000.00", minValue); + var sumValue = await dbContext.ExecuteScalarAsync(sum); + Assert.AreEqual("161000.00", sumValue); + var avgValue = await dbContext.ExecuteScalarAsync(avg); + Assert.AreEqual("6520.000000", avgValue); + var singleValue = await dbContext.ExecuteScalarAsync(singleValueSelect); + Assert.AreEqual("2", singleValue); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteScalarAsync_As_StringReturn_DefaultValue() + { + var noValueReturned = Queries.MySQLQueries.TestDB.ScalarQueries.No_Value_Returned; + var dBNullValue = Queries.MySQLQueries.TestDB.ScalarQueries.DB_Null_Value; + + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + var result = await dbContext.ExecuteScalarAsync(noValueReturned); + Assert.IsInstanceOfType(result); + Assert.AreEqual("", result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(string.Empty, result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteScalarAsync_As_StringReturn_UnsupportedCommands() + { + var sqlStatements = new List + { + Queries.MySQLQueries.TestDB.DDL.Create_Table, + Queries.MySQLQueries.TestDB.DDL.Alter_Table, + Queries.MySQLQueries.TestDB.DDL.Comment_Table, + Queries.MySQLQueries.TestDB.DDL.Truncate_Table, + Queries.MySQLQueries.TestDB.DDL.Drop_Table, + + Queries.MySQLQueries.TestDB.DML.InsertSql, + Queries.MySQLQueries.TestDB.DML.UpdateSql, + Queries.MySQLQueries.TestDB.DML.DeleteSql, + + Queries.MySQLQueries.TestDB.DCL.GrantSql_Command_Table_User, + Queries.MySQLQueries.TestDB.DCL.RevokeSql_Command_Table_User + }; + + foreach (var sqlStatement in sqlStatements) + { + try + { + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + await dbContext.ExecuteScalarAsync(sqlStatement); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("Only SELECT queries are supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteScalar' only supports SELECT queries that have a scalar (single value) return.", ex.AdditionalInfo); + } + } + } + + #endregion + + #region Execute Scalar Tests - << T ExecuteScalar(string sqlStatement) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] @@ -421,90 +769,255 @@ public void Test_MySQL_ExecuteScalar_As_TypedReturn_DefaultValue() Assert.IsInstanceOfType(result); Assert.AreEqual(default(long), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(short), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(uint), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(ulong), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(ushort), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(decimal), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(double), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(float), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(byte), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(bool), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(DateTime), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(Guid), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(string), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(int?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(long?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(short?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(decimal?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(double?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(float?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(bool?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(DateTime?), result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public void Test_MySQL_ExecuteScalar_As_TypedReturn_UnsupportedCommands() + { + var sqlStatements = new List + { + Queries.MySQLQueries.TestDB.DDL.Create_Table, + Queries.MySQLQueries.TestDB.DDL.Alter_Table, + Queries.MySQLQueries.TestDB.DDL.Comment_Table, + Queries.MySQLQueries.TestDB.DDL.Truncate_Table, + Queries.MySQLQueries.TestDB.DDL.Drop_Table, + + Queries.MySQLQueries.TestDB.DML.InsertSql, + Queries.MySQLQueries.TestDB.DML.UpdateSql, + Queries.MySQLQueries.TestDB.DML.DeleteSql, + + Queries.MySQLQueries.TestDB.DCL.GrantSql_Command_Table_User, + Queries.MySQLQueries.TestDB.DCL.RevokeSql_Command_Table_User + }; + + foreach (var sqlStatement in sqlStatements) + { + try + { + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + dbContext.ExecuteScalar(sqlStatement); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("Only SELECT queries are supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteScalar' only supports SELECT queries that have a scalar (single value) return.", ex.AdditionalInfo); + } + } + } + + #endregion + + #region Execute Scalar Async Tests - << Task ExecuteScalarAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteScalarAsync_As_TypedReturn_Scalar_Queries() + { + var countOfRecords = Queries.MySQLQueries.TestDB.ScalarQueries.Count_Of_Records; + var max = Queries.MySQLQueries.TestDB.ScalarQueries.Max; + var min = Queries.MySQLQueries.TestDB.ScalarQueries.Min; + var sum = Queries.MySQLQueries.TestDB.ScalarQueries.Sum; + var avg = Queries.MySQLQueries.TestDB.ScalarQueries.Avg; + var singleValueSelect = Queries.MySQLQueries.TestDB.ScalarQueries.Single_Value_Select; + + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + var count = await dbContext.ExecuteScalarAsync(countOfRecords); + Assert.AreEqual(12, count); + var maxValue = await dbContext.ExecuteScalarAsync(max); + Assert.AreEqual(10000.00, maxValue); + var minValue = await dbContext.ExecuteScalarAsync(min); + Assert.AreEqual(3000.00, minValue); + var sumValue = await dbContext.ExecuteScalarAsync(sum); + Assert.AreEqual(161000.00, sumValue); + var avgValue = await dbContext.ExecuteScalarAsync(avg); + Assert.AreEqual((decimal)6520.000000, avgValue); + var singleValue = await dbContext.ExecuteScalarAsync(singleValueSelect); + Assert.AreEqual("2", singleValue); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteScalarAsync_As_TypedReturn_DefaultValue() + { + var dBNullValue = Queries.MySQLQueries.TestDB.ScalarQueries.DB_Null_Value; + + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + dynamic result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(int), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(long), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(short), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(uint), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(ulong), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(ushort), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(decimal), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(double), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(float), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(byte), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(bool), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(DateTime), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(Guid), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(string), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(int?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(long?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(short?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(decimal?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(double?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(float?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(bool?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(DateTime?), result); } [TestMethod] [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] - public void Test_MySQL_ExecuteScalar_As_TypedReturn_UnsupportedCommands() + public async Task Test_MySQL_ExecuteScalarAsync_As_TypedReturn_UnsupportedCommands() { var sqlStatements = new List { @@ -527,7 +1040,7 @@ public void Test_MySQL_ExecuteScalar_As_TypedReturn_UnsupportedCommands() try { var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); - dbContext.ExecuteScalar(sqlStatement); + await dbContext.ExecuteScalarAsync(sqlStatement); Assert.Fail("No Exception"); } catch (QueryDBException ex) @@ -704,6 +1217,169 @@ public void Test_MySQL_ExecuteCommand_DCL_Queries() #endregion + #region Execute Command Async Tests - << Task ExecuteCommandAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteCommandAsync_DDL_Queries() + { + var createTableSql = Queries.MySQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.MySQLQueries.TestDB.DDL.Alter_Table; + var commentTableSql = Queries.MySQLQueries.TestDB.DDL.Comment_Table; + var commentTableColumnSql = Queries.MySQLQueries.TestDB.DDL.Comment_Table_Column; + var truncateTableSql = Queries.MySQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.MySQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.MySQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.MySQLQueries.TestDB.DDL.DDL_Execute_check; + var dDLTableCommentCheckSql = Queries.MySQLQueries.TestDB.DDL.DDL_Table_Comment_check; + var dDLTableColumnCommentCheckSql = Queries.MySQLQueries.TestDB.DDL.DDL_Table_Column_Comment_check; + + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + await dbContext.ExecuteCommandAsync(createTableSql); + await dbContext.ExecuteCommandAsync(alterTableSql); + await dbContext.ExecuteCommandAsync(commentTableSql); + await dbContext.ExecuteCommandAsync(commentTableColumnSql); + await dbContext.ExecuteCommandAsync(truncateTableSql); + + var tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "mysql", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + var tableComment = await dbContext + .FetchDataAsync(string.Format(dDLTableCommentCheckSql, "mysql", "Employee")); + Assert.AreEqual("This table stores employee records", tableComment[0].ReferenceData["Table_Comment"]); + var tableColumnComment = await dbContext + .FetchDataAsync(string.Format(dDLTableColumnCommentCheckSql, "mysql", "Employee")); + Assert.AreEqual("This column stores employee middle name", tableColumnComment[3].ReferenceData["Table_Column_Comment"]); + + await dbContext.ExecuteCommandAsync(renameTableSql); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "mysql", "Employee")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "mysql", "Employees")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + + await dbContext.ExecuteCommandAsync(dropTableSql); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "mysql", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteCommandAsync_DML_Queries() + { + var insertSql = Queries.MySQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MySQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.MySQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.MySQLQueries.TestDB.DML.VerifyDMLExecution; + + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + // Insert + var rows = await dbContext.ExecuteCommandAsync(insertSql); + Assert.AreEqual(1, rows); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.11", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Update + rows = await dbContext.ExecuteCommandAsync(updateSql); + Assert.AreEqual(1, rows); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.15", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Delete + rows = await dbContext.ExecuteCommandAsync(deleteSql); + Assert.AreEqual(1, rows); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteCommandAsync_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.MySQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + var rows = await dbContext.ExecuteCommandAsync(selectSql); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteCommand' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteCommandAsync_DCL_Queries() + { + var user = "test_user"; + var password = "Test@1234"; + var table = "Agents"; + var commands = "SELECT, UPDATE"; + var checkCommand = "SELECT"; + + var createUser = string.Format(Queries.MySQLQueries.TestDB.DCL.CreateUserSql_User_Password, user, password); + var grantSql = string.Format(Queries.MySQLQueries.TestDB.DCL.GrantSql_Command_Table_User, commands, table, user); + var revokeSql = string.Format(Queries.MySQLQueries.TestDB.DCL.RevokeSql_Command_Table_User, commands, table, user); + var verifyPermissions = string.Format(Queries.MySQLQueries.TestDB.DCL.VerifyPermission_User, user); + var removeUser = string.Format(Queries.MySQLQueries.TestDB.DCL.RemoveUserSql_User, user); + + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + // Create User + var result = await dbContext.ExecuteCommandAsync(createUser); + Assert.AreEqual(0, result); + + // Existing Permissions + var data = await dbContext.FetchDataAsync(verifyPermissions); + Assert.AreEqual(1, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Grant + result = await dbContext.ExecuteCommandAsync(grantSql); + Assert.AreEqual(0, result); + data = await dbContext.FetchDataAsync(verifyPermissions); + Assert.AreEqual(2, data.Count); + Assert.IsTrue(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Revoke + result = await dbContext.ExecuteCommandAsync(revokeSql); + Assert.AreEqual(0, result); + data = await dbContext.FetchDataAsync(verifyPermissions); + Assert.AreEqual(1, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + //Remove User + result = await dbContext.ExecuteCommandAsync(removeUser); + Assert.AreEqual(0, result); + } + + #endregion + #region Execute Transaction Tests - << bool ExecuteTransaction(List sqlStatements) >> [TestMethod] @@ -726,7 +1402,7 @@ public void Test_MySQL_ExecuteTransaction_DDL_Multiple_Queries() }; var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); var result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); var tableCount = dbContext .FetchData(string.Format(dDLExecutionCheckSql, "mysql", "Employee")); @@ -739,7 +1415,7 @@ public void Test_MySQL_ExecuteTransaction_DDL_Multiple_Queries() dropTableSql }; result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); tableCount = dbContext .FetchData(string.Format(dDLExecutionCheckSql, "mysql", "Employees")); @@ -764,7 +1440,7 @@ public void Test_MySQL_ExecuteTransaction_DML_Multiple_Queries() // Insert & Update var result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); var data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(1, data.Count); var agent = data.FirstOrDefault(); @@ -781,7 +1457,7 @@ public void Test_MySQL_ExecuteTransaction_DML_Multiple_Queries() deleteSql }; result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(0, data.Count); } @@ -805,7 +1481,8 @@ public void Test_MySQL_ExecuteTransaction_Incomplete_Transaction_Rollback_On_Err // Insert & Update var result = dbContext.ExecuteTransaction(statements); - Assert.IsFalse(result); + Assert.IsFalse(result.Success); + Assert.AreEqual("You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1", result.Exception.Message); var data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(0, data.Count); } @@ -837,6 +1514,140 @@ public void Test_MySQL_ExecuteTransaction_DML_Unsupported_SELECT_Queries() #endregion + #region Execute Transaction Async Tests - << Task ExecuteTransactionAsync(List sqlStatements) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteTransactionAsync_DDL_Multiple_Queries() + { + var createTableSql = Queries.MySQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.MySQLQueries.TestDB.DDL.Alter_Table; + var truncateTableSql = Queries.MySQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.MySQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.MySQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.MySQLQueries.TestDB.DDL.DDL_Execute_check; + + // Create, Alter & Truncate + var statements = new List + { + createTableSql, + alterTableSql, + truncateTableSql + }; + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + + var tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "mysql", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["Table_Count"]); + + // Rename & Drop + statements = new List + { + renameTableSql, + dropTableSql + }; + result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "mysql", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["Table_Count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteTransactionAsync_DML_Multiple_Queries() + { + var insertSql = Queries.MySQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MySQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.MySQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.MySQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql + }; + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + // Insert & Update + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["Agent_Code"]); + Assert.AreEqual("John", agent.ReferenceData["Agent_Name"]); + Assert.AreEqual("Wick", agent.ReferenceData["Working_Area"]); + Assert.AreEqual("0.15", agent.ReferenceData["Commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["Phone_No"]); + Assert.AreEqual("", agent.ReferenceData["Country"]); + + // Delete + statements = new List + { + deleteSql + }; + result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteTransactionAsync_Incomplete_Transaction_Rollback_On_Error() + { + var insertSql = Queries.MySQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.MySQLQueries.TestDB.DML.UpdateSql; + var updateErrorSql = "UPDATE"; + var verifyDMLExecution = Queries.MySQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql, + updateErrorSql + }; + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + + // Insert & Update + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsFalse(result.Success); + Assert.AreEqual("You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1", result.Exception.Message); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(MYSQL_TESTS)] + public async Task Test_MySQL_ExecuteTransactionAsync_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.MySQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var statements = new List + { + selectSql + }; + var dbContext = new DBContext(DB.MySQL, MySQLConnectionString); + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteTransaction' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + #endregion + #endregion } diff --git a/QueryDB.Core.Tests/OracleTests.cs b/QueryDB.Core.Tests/OracleTests.cs index af5c9b8..d9c0e84 100644 --- a/QueryDB.Core.Tests/OracleTests.cs +++ b/QueryDB.Core.Tests/OracleTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace QueryDB.Core.Tests { @@ -165,7 +166,141 @@ public void Test_Oracle_FetchData_Dictionary_DataTypes_Check() #endregion - #region Fetch Data Tests - << List FetchData(string selectSql) >> + #region Fetch Data Async Tests - << List FetchData(string selectSql, bool upperCaseKeys = false) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Dictionary_SelectQuery() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_NAME"] == "Benjamin"); + Assert.AreEqual("A009", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Benjamin", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Hampshair", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.11", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("008-22536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Dictionary_SelectQuery_UpperCaseKeys() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_NAME"] == "Benjamin"); + Assert.AreEqual("A009", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Benjamin", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Hampshair", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.11", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("008-22536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Dictionary_SelectQuery_Joins() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_CODE"] == "A004" && X.ReferenceData["CUST_CODE"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Ivan", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("C00006", agent.ReferenceData["CUST_CODE"]); + Assert.AreEqual("Shilton", agent.ReferenceData["CUST_NAME"]); + Assert.AreEqual("200104", agent.ReferenceData["ORD_NUM"]); + Assert.AreEqual("1500", agent.ReferenceData["ORD_AMOUNT"]); + Assert.AreEqual("500", agent.ReferenceData["ADVANCE_AMOUNT"]); + Assert.AreEqual("SOD", agent.ReferenceData["ORD_DESCRIPTION"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Dictionary_SelectQuery_Joins_UpperCaseKeys() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_CODE"] == "A004" && X.ReferenceData["CUST_CODE"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Ivan", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("C00006", agent.ReferenceData["CUST_CODE"]); + Assert.AreEqual("Shilton", agent.ReferenceData["CUST_NAME"]); + Assert.AreEqual("200104", agent.ReferenceData["ORD_NUM"]); + Assert.AreEqual("1500", agent.ReferenceData["ORD_AMOUNT"]); + Assert.AreEqual("500", agent.ReferenceData["ADVANCE_AMOUNT"]); + Assert.AreEqual("SOD", agent.ReferenceData["ORD_DESCRIPTION"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Dictionary_SelectQuery_Aliases() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_CODE"] == "A004" && X.ReferenceData["CUST_CODE"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Ivan", agent.ReferenceData["AGENT"]); + Assert.AreEqual("Torento", agent.ReferenceData["AGENT_LOCATION"]); + Assert.AreEqual("C00006", agent.ReferenceData["CUST_CODE"]); + Assert.AreEqual("Shilton", agent.ReferenceData["CUSTOMER"]); + Assert.AreEqual("Torento", agent.ReferenceData["CUSTOMER_LOCATION"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Dictionary_SelectQuery_Aliases_UpperCaseKeys() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_CODE"] == "A004" && X.ReferenceData["CUST_CODE"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Ivan", agent.ReferenceData["AGENT"]); + Assert.AreEqual("Torento", agent.ReferenceData["AGENT_LOCATION"]); + Assert.AreEqual("C00006", agent.ReferenceData["CUST_CODE"]); + Assert.AreEqual("Shilton", agent.ReferenceData["CUSTOMER"]); + Assert.AreEqual("Torento", agent.ReferenceData["CUSTOMER_LOCATION"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Dictionary_DataTypes_Check() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql_DataTypes; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(2, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual(GetBase64Content(Environment.CurrentDirectory + "/SeedData/oracle.sql"), dataType.ReferenceData["BFILE_COLUMN"]); + Assert.AreEqual("3q2+7w==", dataType.ReferenceData["BLOB_COLUMN"]); + Assert.AreEqual("A", dataType.ReferenceData["CHAR_COLUMN"]); + Assert.AreEqual("Sample CLOB data", dataType.ReferenceData["CLOB_COLUMN"]); + Assert.AreEqual("09/21/2024 00:00:00", ConvertToUSFormat(dataType.ReferenceData["DATE_COLUMN"])); + Assert.AreEqual("123.45", dataType.ReferenceData["FLOAT_COLUMN"]); + Assert.AreEqual("123", dataType.ReferenceData["INTEGER_COLUMN"]); + Assert.AreEqual("14", dataType.ReferenceData["INTERVALYEARTOMONTH_COLUMN"]); + Assert.AreEqual("1.02:03:04.5000000", dataType.ReferenceData["INTERNALDAYTOSECOND_COLUMN"]); + Assert.AreEqual("Sample LONG data", dataType.ReferenceData["LONG_COLUMN"]); + Assert.AreEqual("A", dataType.ReferenceData["NCHAR_COLUMN"]); + Assert.AreEqual("Sample NCLOB data", dataType.ReferenceData["NCLOB_COLUMN"]); + Assert.AreEqual("123.45", dataType.ReferenceData["NUMBER_COLUMN"]); + Assert.AreEqual("Sample NVARCHAR2 data", dataType.ReferenceData["NVARCHAR2_COLUMN"]); + Assert.AreEqual("3q2+7w==", dataType.ReferenceData["RAW_COLUMN"]); + Assert.AreEqual("09/21/2024 12:34:56", ConvertToUSFormat(dataType.ReferenceData["TIMESTAMP_COLUMN"])); + Assert.AreEqual("09/21/2024 12:34:56", ConvertToUSFormat(dataType.ReferenceData["TIMESTAMPWITHTIMEZONE_COLUMN"])); + Assert.AreEqual("09/21/2024 12:34:56", ConvertToUSFormat(dataType.ReferenceData["TIMESTAMPWITHLOCALTIMEZONE_COLUMN"])); + Assert.AreEqual("Sample VARCHAR data", dataType.ReferenceData["VARCHAR_COLUMN"]); + Assert.AreEqual("Sample VARCHAR2 data", dataType.ReferenceData["VARCHAR2_COLUMN"]); + } + + #endregion + + #region Fetch Data Tests - << List FetchData(string selectSql, bool strict = false) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] @@ -283,6 +418,135 @@ public void Test_Oracle_FetchData_Entity_Strict_Error_Check() try { var data = new DBContext(DB.Oracle, OracleConnectionString).FetchData(selectSql, strict: true); + Assert.Fail("No Exception"); + } + catch (IndexOutOfRangeException ex) + { + Assert.AreEqual("Unable to find specified column in result set", ex.Message); + } + } + + #endregion + + #region Fetch Data Async Tests - << List FetchData(string selectSql, bool strict = false) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Entity_SelectQuery() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Name == "Benjamin"); + Assert.AreEqual("A009", agent.Agent_Code); + Assert.AreEqual("Benjamin", agent.Agent_Name); + Assert.AreEqual("Hampshair", agent.Working_Area); + Assert.AreEqual(0.11, agent.Commission); + Assert.AreEqual("008-22536178", agent.Phone_No); + Assert.IsNull(agent.Country); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Entity_SelectQuery_Joins() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Code == "A004" && X.Cust_Code == "C00006"); + Assert.AreEqual("A004", agent.Agent_Code); + Assert.AreEqual("Ivan", agent.Agent_Name); + Assert.AreEqual("C00006", agent.Cust_Code); + Assert.AreEqual("Shilton", agent.Cust_Name); + Assert.AreEqual(200104, agent.Ord_Num); + Assert.AreEqual(1500.00, agent.Ord_Amount); + Assert.AreEqual(500.00, agent.Advance_Amount); + Assert.AreEqual("SOD", agent.Ord_Description); + // Non Existent Query Data + Assert.IsNull(agent.Agent); + Assert.IsNull(agent.Agent_Location); + Assert.IsNull(agent.Customer); + Assert.IsNull(agent.Customer_Location); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Entity_SelectQuery_Aliases() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Code == "A004" && X.Cust_Code == "C00006"); + Assert.AreEqual("A004", agent.Agent_Code); + Assert.AreEqual("Ivan", agent.Agent); + Assert.AreEqual("Torento", agent.Agent_Location); + Assert.AreEqual("C00006", agent.Cust_Code); + Assert.AreEqual("Shilton", agent.Customer); + Assert.AreEqual("Torento", agent.Customer_Location); + // Non Existent Query Data + Assert.IsNull(agent.Agent_Name); + Assert.IsNull(agent.Cust_Name); + Assert.AreEqual(0, agent.Ord_Num); + Assert.AreEqual(0, agent.Ord_Amount); + Assert.AreEqual(0, agent.Advance_Amount); + Assert.IsNull(agent.Ord_Description); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Entity_DataTypes_Check() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql_DataTypes; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(2, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual(GetBase64Content(Environment.CurrentDirectory + "/SeedData/oracle.sql"), ConvertByteArrayToBase64(dataType.BFile_Column)); + Assert.AreEqual("3q2+7w==", ConvertByteArrayToBase64(dataType.Blob_Column)); + Assert.AreEqual("A", dataType.Char_Column); + Assert.AreEqual("Sample CLOB data", dataType.Clob_Column); + Assert.AreEqual("09/21/2024 00:00:00", ConvertToUSFormat(dataType.Date_Column.ToString())); + Assert.AreEqual((decimal)123.45, dataType.Float_Column); + Assert.AreEqual(123, (int)dataType.Integer_Column); + Assert.AreEqual(14, dataType.IntervalYearToMonth_Column); + Assert.AreEqual("1.02:03:04.5000000", dataType.InternalDayToSecond_Column.ToString()); + Assert.AreEqual("Sample LONG data", dataType.Long_Column); + Assert.AreEqual("A", dataType.NChar_Column); + Assert.AreEqual("Sample NCLOB data", dataType.NClob_Column); + Assert.AreEqual((decimal)123.45, dataType.Number_Column); + Assert.AreEqual("Sample NVARCHAR2 data", dataType.NVarchar2_Column); + Assert.AreEqual("3q2+7w==", ConvertByteArrayToBase64(dataType.Raw_Column)); + Assert.AreEqual("09/21/2024 12:34:56", ConvertToUSFormat(dataType.Timestamp_Column.ToString())); + Assert.AreEqual("09/21/2024 12:34:56", ConvertToUSFormat(dataType.TimestampWithTimeZone_Column.ToString())); + Assert.AreEqual("09/21/2024 12:34:56", ConvertToUSFormat(dataType.TimestampWithLocalTimeZone_Column.ToString())); + Assert.AreEqual("Sample VARCHAR data", dataType.Varchar_Column); + Assert.AreEqual("Sample VARCHAR2 data", dataType.Varchar2_Column); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Entity_Strict_Check() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql_Strict; + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql, strict: true); + Assert.AreEqual(34, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual("A003", dataType.Agent_Code); + Assert.AreEqual("Alex", dataType.Agent); + Assert.AreEqual("C00013", dataType.Cust_Code); + Assert.AreEqual("Holmes", dataType.Customer); + Assert.AreEqual(200100, dataType.Ord_Num); + Assert.AreEqual(1000.00, dataType.Ord_Amount); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_FetchDataAsync_Entity_Strict_Error_Check() + { + var selectSql = Queries.OracleQueries.TestDB.SelectSql_Strict; + try + { + var data = await new DBContext(DB.Oracle, OracleConnectionString).FetchDataAsync(selectSql, strict: true); + Assert.Fail("No Exception"); } catch (IndexOutOfRangeException ex) { @@ -292,7 +556,7 @@ public void Test_Oracle_FetchData_Entity_Strict_Error_Check() #endregion - #region Execute Scalar Tests - << string ExecuteScalar(string sqlStatement); >> + #region Execute Scalar Tests - << string ExecuteScalar(string sqlStatement) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] @@ -378,7 +642,93 @@ public void Test_Oracle_ExecuteScalar_As_StringReturn_UnsupportedCommands() #endregion - #region Execute Scalar Tests - << T ExecuteScalar(string sqlStatement); >> + #region Execute Scalar Async Tests - << Task ExecuteScalarAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteScalarAsync_As_StringReturn_Scalar_Queries() + { + var countOfRecords = Queries.OracleQueries.TestDB.ScalarQueries.Count_Of_Records; + var max = Queries.OracleQueries.TestDB.ScalarQueries.Max; + var min = Queries.OracleQueries.TestDB.ScalarQueries.Min; + var sum = Queries.OracleQueries.TestDB.ScalarQueries.Sum; + var avg = Queries.OracleQueries.TestDB.ScalarQueries.Avg; + var singleValueSelect = Queries.OracleQueries.TestDB.ScalarQueries.Single_Value_Select; + + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + var count = await dbContext.ExecuteScalarAsync(countOfRecords); + Assert.AreEqual("12", count); + var maxValue = await dbContext.ExecuteScalarAsync(max); + Assert.AreEqual("10000", maxValue); + var minValue = await dbContext.ExecuteScalarAsync(min); + Assert.AreEqual("3000", minValue); + var sumValue = await dbContext.ExecuteScalarAsync(sum); + Assert.AreEqual("161000", sumValue); + var avgValue = await dbContext.ExecuteScalarAsync(avg); + Assert.AreEqual("6520", avgValue); + var singleValue = await dbContext.ExecuteScalarAsync(singleValueSelect); + Assert.AreEqual("2", singleValue); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteScalarAsync_As_StringReturn_DefaultValue() + { + var noValueReturned = Queries.OracleQueries.TestDB.ScalarQueries.No_Value_Returned; + var dBNullValue = Queries.OracleQueries.TestDB.ScalarQueries.DB_Null_Value; + + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + var result = await dbContext.ExecuteScalarAsync(noValueReturned); + Assert.IsInstanceOfType(result); + Assert.AreEqual("", result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(string.Empty, result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteScalarAsync_As_StringReturn_UnsupportedCommands() + { + var sqlStatements = new List + { + Queries.OracleQueries.TestDB.DDL.Create_Table, + Queries.OracleQueries.TestDB.DDL.Alter_Table, + Queries.OracleQueries.TestDB.DDL.Comment_Table, + Queries.OracleQueries.TestDB.DDL.Truncate_Table, + Queries.OracleQueries.TestDB.DDL.Drop_Table, + + Queries.OracleQueries.TestDB.DML.InsertSql, + Queries.OracleQueries.TestDB.DML.UpdateSql, + Queries.OracleQueries.TestDB.DML.DeleteSql, + + Queries.OracleQueries.TestDB.DCL.GrantSql_Command_Table_User, + Queries.OracleQueries.TestDB.DCL.RevokeSql_Command_Table_User + }; + + foreach (var sqlStatement in sqlStatements) + { + try + { + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + await dbContext.ExecuteScalarAsync(sqlStatement); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("Only SELECT queries are supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteScalar' only supports SELECT queries that have a scalar (single value) return.", ex.AdditionalInfo); + } + } + } + + #endregion + + #region Execute Scalar Tests - << T ExecuteScalar(string sqlStatement) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] @@ -439,74 +789,239 @@ public void Test_Oracle_ExecuteScalar_As_TypedReturn_DefaultValue() Assert.IsInstanceOfType(result); Assert.AreEqual(default(ushort), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(decimal), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(double), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(float), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(byte), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(bool), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(DateTime), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(Guid), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(string), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(int?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(long?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(short?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(decimal?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(double?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(float?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(bool?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(DateTime?), result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public void Test_Oracle_ExecuteScalar_As_TypedReturn_UnsupportedCommands() + { + var sqlStatements = new List + { + Queries.OracleQueries.TestDB.DDL.Create_Table, + Queries.OracleQueries.TestDB.DDL.Alter_Table, + Queries.OracleQueries.TestDB.DDL.Comment_Table, + Queries.OracleQueries.TestDB.DDL.Truncate_Table, + Queries.OracleQueries.TestDB.DDL.Drop_Table, + + Queries.OracleQueries.TestDB.DML.InsertSql, + Queries.OracleQueries.TestDB.DML.UpdateSql, + Queries.OracleQueries.TestDB.DML.DeleteSql, + + Queries.OracleQueries.TestDB.DCL.GrantSql_Command_Table_User, + Queries.OracleQueries.TestDB.DCL.RevokeSql_Command_Table_User + }; + + foreach (var sqlStatement in sqlStatements) + { + try + { + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + dbContext.ExecuteScalar(sqlStatement); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("Only SELECT queries are supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteScalar' only supports SELECT queries that have a scalar (single value) return.", ex.AdditionalInfo); + } + } + } + + #endregion + + #region Execute Scalar Async Tests - << Task ExecuteScalarAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteScalarAsync_As_TypedReturn_Scalar_Queries() + { + var countOfRecords = Queries.OracleQueries.TestDB.ScalarQueries.Count_Of_Records; + var max = Queries.OracleQueries.TestDB.ScalarQueries.Max; + var min = Queries.OracleQueries.TestDB.ScalarQueries.Min; + var sum = Queries.OracleQueries.TestDB.ScalarQueries.Sum; + var avg = Queries.OracleQueries.TestDB.ScalarQueries.Avg; + var singleValueSelect = Queries.OracleQueries.TestDB.ScalarQueries.Single_Value_Select; + + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + var count = await dbContext.ExecuteScalarAsync(countOfRecords); + Assert.AreEqual(12, count); + var maxValue = await dbContext.ExecuteScalarAsync(max); + Assert.AreEqual(10000.00, maxValue); + var minValue = await dbContext.ExecuteScalarAsync(min); + Assert.AreEqual(3000.00, minValue); + var sumValue = await dbContext.ExecuteScalarAsync(sum); + Assert.AreEqual(161000.00, sumValue); + var avgValue = await dbContext.ExecuteScalarAsync(avg); + Assert.AreEqual((decimal)6520.000000, avgValue); + var singleValue = await dbContext.ExecuteScalarAsync(singleValueSelect); + Assert.AreEqual("2", singleValue); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteScalarAsync_As_TypedReturn_DefaultValue() + { + var dBNullValue = Queries.OracleQueries.TestDB.ScalarQueries.DB_Null_Value; + + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + dynamic result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(int), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(long), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(short), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(uint), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(ulong), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(ushort), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(decimal), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(double), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(float), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(byte), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(bool), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(DateTime), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(Guid), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(string), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(int?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(long?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(short?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(decimal?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(double?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(float?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(bool?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(DateTime?), result); } [TestMethod] [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] - public void Test_Oracle_ExecuteScalar_As_TypedReturn_UnsupportedCommands() + public async Task Test_Oracle_ExecuteScalarAsync_As_TypedReturn_UnsupportedCommands() { var sqlStatements = new List { @@ -529,7 +1044,7 @@ public void Test_Oracle_ExecuteScalar_As_TypedReturn_UnsupportedCommands() try { var dbContext = new DBContext(DB.Oracle, OracleConnectionString); - dbContext.ExecuteScalar(sqlStatement); + await dbContext.ExecuteScalarAsync(sqlStatement); Assert.Fail("No Exception"); } catch (QueryDBException ex) @@ -715,6 +1230,178 @@ public void Test_Oracle_ExecuteCommand_DCL_Queries() #endregion + #region Execute Command Async Tests - << Task ExecuteCommandAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteCommandAsync_DDL_Queries() + { + var createTableSql = Queries.OracleQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.OracleQueries.TestDB.DDL.Alter_Table; + var commentTableSql = Queries.OracleQueries.TestDB.DDL.Comment_Table; + var commentTableColumnSql = Queries.OracleQueries.TestDB.DDL.Comment_Table_Column; + var truncateTableSql = Queries.OracleQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.OracleQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.OracleQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.OracleQueries.TestDB.DDL.DDL_Execute_check; + var dDLTableCommentCheckSql = Queries.OracleQueries.TestDB.DDL.DDL_Table_Comment_check; + var dDLTableColumnCommentCheckSql = Queries.OracleQueries.TestDB.DDL.DDL_Table_Column_Comment_check; + + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + await dbContext.ExecuteCommandAsync(createTableSql); + await dbContext.ExecuteCommandAsync(alterTableSql); + await dbContext.ExecuteCommandAsync(commentTableSql); + await dbContext.ExecuteCommandAsync(commentTableColumnSql); + await dbContext.ExecuteCommandAsync(truncateTableSql); + + var tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["TABLE_COUNT"]); + var tableComment = await dbContext + .FetchDataAsync(string.Format(dDLTableCommentCheckSql, "Employee")); + Assert.AreEqual("This table stores employee records", tableComment[0].ReferenceData["TABLE_COMMENT"]); + var tableColumnComment = await dbContext + .FetchDataAsync(string.Format(dDLTableColumnCommentCheckSql, "Employee")); + Assert.AreEqual("This column stores employee middle name", tableColumnComment[3].ReferenceData["TABLE_COLUMN_COMMENT"]); + + await dbContext.ExecuteCommandAsync(renameTableSql); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "Employee")); + Assert.AreEqual("0", tableCount[0].ReferenceData["TABLE_COUNT"]); + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "Employees")); + Assert.AreEqual("1", tableCount[0].ReferenceData["TABLE_COUNT"]); + + await dbContext.ExecuteCommandAsync(dropTableSql); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["TABLE_COUNT"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteCommandAsync_DML_Queries() + { + var insertSql = Queries.OracleQueries.TestDB.DML.InsertSql; + var updateSql = Queries.OracleQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.OracleQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.OracleQueries.TestDB.DML.VerifyDMLExecution; + + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + // Insert + var rows = await dbContext.ExecuteCommandAsync(insertSql); + Assert.AreEqual(1, rows); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("John", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Wick", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.11", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + + // Update + rows = await dbContext.ExecuteCommandAsync(updateSql); + Assert.AreEqual(1, rows); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("John", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Wick", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.15", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + + // Delete + rows = await dbContext.ExecuteCommandAsync(deleteSql); + Assert.AreEqual(1, rows); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteCommandAsync_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.OracleQueries.TestDB.DML.SelectSql; + + // Select + try + { + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + var rows = await dbContext.ExecuteCommandAsync(selectSql); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteCommand' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + [TestMethod] + [TestCategory(ORACLE_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteCommandAsync_DCL_Queries() + { + var user = "C##TEST_USER"; + var password = "Test123456"; + var table = "AGENTS"; + var commands = "SELECT, UPDATE"; + var checkCommand = "SELECT"; + + var createUser = string.Format(Queries.OracleQueries.TestDB.DCL.CreateUserSql_User_Password, user, password); + var grantConnect = string.Format(Queries.OracleQueries.TestDB.DCL.GrantConnectSql_User, user); + var grantSql = string.Format(Queries.OracleQueries.TestDB.DCL.GrantSql_Command_Table_User, commands, table, user); + var revokeSql = string.Format(Queries.OracleQueries.TestDB.DCL.RevokeSql_Command_Table_User, commands, table, user); + var verifyPermissions = string.Format(Queries.OracleQueries.TestDB.DCL.VerifyPermission_User, user); + var removeUser = string.Format(Queries.OracleQueries.TestDB.DCL.RemoveUserSql_User, user); + + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + // Create User + var result = await dbContext.ExecuteCommandAsync(createUser); + Assert.AreEqual(0, result); + + // Grant CONNECT to User + result = await dbContext.ExecuteCommandAsync("CREATE ROLE CONNECT"); + result = await dbContext.ExecuteCommandAsync($"GRANT CONNECT, RESOURCE TO {user}"); + //result = await dbContext.ExecuteCommandAsync($"GRANT CREATE SEQUENCE TO {user}"); + //result = await dbContext.ExecuteCommandAsync($"GRANT CREATE SYNONYM TO {user}"); + //result = await dbContext.ExecuteCommandAsync($"GRANT UNLIMITED TABLESPACE TO {user}"); + //Assert.AreEqual(0, result); + + // Existing Permissions + var data = await dbContext.FetchDataAsync(verifyPermissions); + Assert.AreEqual(1, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Grant + result = await dbContext.ExecuteCommandAsync(grantSql); + Assert.AreEqual(0, result); + data = await dbContext.FetchDataAsync(verifyPermissions); + Assert.AreEqual(2, data.Count); + Assert.IsTrue(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Revoke + result = await dbContext.ExecuteCommandAsync(revokeSql); + Assert.AreEqual(0, result); + data = await dbContext.FetchDataAsync(verifyPermissions); + Assert.AreEqual(1, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Remove User + result = await dbContext.ExecuteCommandAsync(removeUser); + Assert.AreEqual(0, result); + } + + #endregion + #region Execute Transaction Tests - << bool ExecuteTransaction(List sqlStatements) >> [TestMethod] @@ -737,7 +1424,7 @@ public void Test_Oracle_ExecuteTransaction_DDL_Multiple_Queries() }; var dbContext = new DBContext(DB.Oracle, OracleConnectionString); var result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); var tableCount = dbContext .FetchData(string.Format(dDLExecutionCheckSql, "Employee")); @@ -750,7 +1437,7 @@ public void Test_Oracle_ExecuteTransaction_DDL_Multiple_Queries() dropTableSql }; result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); tableCount = dbContext .FetchData(string.Format(dDLExecutionCheckSql, "Employees")); @@ -775,7 +1462,7 @@ public void Test_Oracle_ExecuteTransaction_DML_Multiple_Queries() // Insert & Update var result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); var data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(1, data.Count); var agent = data.FirstOrDefault(); @@ -792,7 +1479,7 @@ public void Test_Oracle_ExecuteTransaction_DML_Multiple_Queries() deleteSql }; result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(0, data.Count); } @@ -816,7 +1503,8 @@ public void Test_Oracle_ExecuteTransaction_Incomplete_Transaction_Rollback_On_Er // Insert & Update var result = dbContext.ExecuteTransaction(statements); - Assert.IsFalse(result); + Assert.IsFalse(result.Success); + Assert.AreEqual("ORA-00903: invalid table name", result.Exception.Message); var data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(0, data.Count); } @@ -848,6 +1536,140 @@ public void Test_Oracle_ExecuteTransaction_DML_Unsupported_SELECT_Queries() #endregion + #region Execute Transaction Async Tests - << Task ExecuteTransactionAsync(List sqlStatements) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteTransactionAsync_DDL_Multiple_Queries() + { + var createTableSql = Queries.OracleQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.OracleQueries.TestDB.DDL.Alter_Table; + var truncateTableSql = Queries.OracleQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.OracleQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.OracleQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.OracleQueries.TestDB.DDL.DDL_Execute_check; + + // Create, Alter & Truncate + var statements = new List + { + createTableSql, + alterTableSql, + truncateTableSql + }; + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + + var tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["TABLE_COUNT"]); + + // Rename & Drop + statements = new List + { + renameTableSql, + dropTableSql + }; + result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["TABLE_COUNT"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteTransactionAsync_DML_Multiple_Queries() + { + var insertSql = Queries.OracleQueries.TestDB.DML.InsertSql; + var updateSql = Queries.OracleQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.OracleQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.OracleQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql + }; + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + // Insert & Update + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("John", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Wick", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.15", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + + // Delete + statements = new List + { + deleteSql + }; + result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteTransactionAsync_Incomplete_Transaction_Rollback_On_Error() + { + var insertSql = Queries.OracleQueries.TestDB.DML.InsertSql; + var updateSql = Queries.OracleQueries.TestDB.DML.UpdateSql; + var updateErrorSql = "UPDATE"; + var verifyDMLExecution = Queries.OracleQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql, + updateErrorSql + }; + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + + // Insert & Update + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsFalse(result.Success); + Assert.AreEqual("ORA-00903: invalid table name", result.Exception.Message); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(ORACLE_TESTS)] + public async Task Test_Oracle_ExecuteTransactionAsync_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.OracleQueries.TestDB.DML.SelectSql; + + // Select + try + { + var statements = new List + { + selectSql + }; + var dbContext = new DBContext(DB.Oracle, OracleConnectionString); + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteTransaction' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + #endregion + #endregion } diff --git a/QueryDB.Core.Tests/PostgreSQLTests.cs b/QueryDB.Core.Tests/PostgreSQLTests.cs index 73678d9..cb04a28 100644 --- a/QueryDB.Core.Tests/PostgreSQLTests.cs +++ b/QueryDB.Core.Tests/PostgreSQLTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace QueryDB.Core.Tests { @@ -161,7 +162,137 @@ public void Test_PostgreSQL_FetchData_Dictionary_DataTypes_Check() #endregion - #region Fetch Data Tests - << List FetchData(string selectSql) >> + #region Fetch Data Async Tests - << Task> FetchDataAsync(string selectSql, bool upperCaseKeys = false) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Dictionary_SelectQuery() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["agent_name"] == "Benjamin"); + Assert.AreEqual("A009", agent.ReferenceData["agent_code"]); + Assert.AreEqual("Benjamin", agent.ReferenceData["agent_name"]); + Assert.AreEqual("Hampshair", agent.ReferenceData["working_area"]); + Assert.AreEqual("0.11", agent.ReferenceData["commission"]); + Assert.AreEqual("008-22536178", agent.ReferenceData["phone_no"]); + Assert.AreEqual("", agent.ReferenceData["country"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Dictionary_SelectQuery_UpperCaseKeys() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_NAME"] == "Benjamin"); + Assert.AreEqual("A009", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Benjamin", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("Hampshair", agent.ReferenceData["WORKING_AREA"]); + Assert.AreEqual("0.11", agent.ReferenceData["COMMISSION"]); + Assert.AreEqual("008-22536178", agent.ReferenceData["PHONE_NO"]); + Assert.AreEqual("", agent.ReferenceData["COUNTRY"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Dictionary_SelectQuery_Joins() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["agent_code"] == "A004" && X.ReferenceData["cust_code"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["agent_code"]); + Assert.AreEqual("Ivan", agent.ReferenceData["agent_name"]); + Assert.AreEqual("C00006", agent.ReferenceData["cust_code"]); + Assert.AreEqual("Shilton", agent.ReferenceData["cust_name"]); + Assert.AreEqual("200104", agent.ReferenceData["ord_num"]); + Assert.AreEqual("1500.00", agent.ReferenceData["ord_amount"]); + Assert.AreEqual("500.00", agent.ReferenceData["advance_amount"]); + Assert.AreEqual("SOD", agent.ReferenceData["ord_description"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Dictionary_SelectQuery_Joins_UpperCaseKeys() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_CODE"] == "A004" && X.ReferenceData["CUST_CODE"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Ivan", agent.ReferenceData["AGENT_NAME"]); + Assert.AreEqual("C00006", agent.ReferenceData["CUST_CODE"]); + Assert.AreEqual("Shilton", agent.ReferenceData["CUST_NAME"]); + Assert.AreEqual("200104", agent.ReferenceData["ORD_NUM"]); + Assert.AreEqual("1500.00", agent.ReferenceData["ORD_AMOUNT"]); + Assert.AreEqual("500.00", agent.ReferenceData["ADVANCE_AMOUNT"]); + Assert.AreEqual("SOD", agent.ReferenceData["ORD_DESCRIPTION"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Dictionary_SelectQuery_Aliases() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["agent_code"] == "A004" && X.ReferenceData["cust_code"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["agent_code"]); + Assert.AreEqual("Ivan", agent.ReferenceData["agent"]); + Assert.AreEqual("Torento", agent.ReferenceData["agent_location"]); + Assert.AreEqual("C00006", agent.ReferenceData["cust_code"]); + Assert.AreEqual("Shilton", agent.ReferenceData["customer"]); + Assert.AreEqual("Torento", agent.ReferenceData["customer_location"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Dictionary_SelectQuery_Aliases_UpperCaseKeys() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql, upperCaseKeys: true); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.ReferenceData["AGENT_CODE"] == "A004" && X.ReferenceData["CUST_CODE"] == "C00006"); + Assert.AreEqual("A004", agent.ReferenceData["AGENT_CODE"]); + Assert.AreEqual("Ivan", agent.ReferenceData["AGENT"]); + Assert.AreEqual("Torento", agent.ReferenceData["AGENT_LOCATION"]); + Assert.AreEqual("C00006", agent.ReferenceData["CUST_CODE"]); + Assert.AreEqual("Shilton", agent.ReferenceData["CUSTOMER"]); + Assert.AreEqual("Torento", agent.ReferenceData["CUSTOMER_LOCATION"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Dictionary_DataTypes_Check() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql_DataTypes; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(1, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual("9223372036854775807", dataType.ReferenceData["bigint_column"]); + Assert.AreEqual("True", dataType.ReferenceData["boolean_column"]); + Assert.AreEqual("3q2+7w==", dataType.ReferenceData["bytea_column"]); + Assert.AreEqual("char10", dataType.ReferenceData["char_column"]); + Assert.AreEqual("varchar50", dataType.ReferenceData["varchar_column"]); + Assert.AreEqual("09/21/2024 00:00:00", ConvertToUSFormat(dataType.ReferenceData["date_column"])); + Assert.AreEqual("123456.789", dataType.ReferenceData["double_column"]); + Assert.AreEqual("2147483647", dataType.ReferenceData["int_column"]); + Assert.AreEqual("12345.67", dataType.ReferenceData["money_column"]); + Assert.AreEqual("123456.789", dataType.ReferenceData["numeric_column"]); + Assert.AreEqual("12345.67", dataType.ReferenceData["real_column"]); + Assert.AreEqual("32767", dataType.ReferenceData["smallint_column"]); + Assert.AreEqual("some text", dataType.ReferenceData["text_column"]); + Assert.AreEqual("12:34:56", dataType.ReferenceData["time_column"]); + Assert.AreEqual("09/21/2024 14:34:56", ConvertToUSFormat(dataType.ReferenceData["timestamp_column"])); + Assert.AreEqual("123e4567-e89b-12d3-a456-426614174000", dataType.ReferenceData["uuid_column"]); + } + + #endregion + + #region Fetch Data Tests - << List FetchData(string selectSql, bool strict = false) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] @@ -275,6 +406,131 @@ public void Test_PostgreSQL_FetchData_Entity_Strict_Error_Check() try { var data = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchData(selectSql, strict: true); + Assert.Fail("No Exception"); + } + catch (IndexOutOfRangeException ex) + { + Assert.AreEqual("Field not found in row: Agent_Name", ex.Message); + } + } + + #endregion + + #region Fetch Data Async Tests - << Task> FetchDataAsync(string selectSql, bool strict = false) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Entity_SelectQuery() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(12, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Name == "Benjamin"); + Assert.AreEqual("A009", agent.Agent_Code); + Assert.AreEqual("Benjamin", agent.Agent_Name); + Assert.AreEqual("Hampshair", agent.Working_Area); + Assert.AreEqual((decimal)0.11, agent.Commission); + Assert.AreEqual("008-22536178", agent.Phone_No); + Assert.AreEqual("", agent.Country); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Entity_SelectQuery_Joins() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql_Join; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Code == "A004" && X.Cust_Code == "C00006"); + Assert.AreEqual("A004", agent.Agent_Code); + Assert.AreEqual("Ivan", agent.Agent_Name); + Assert.AreEqual("C00006", agent.Cust_Code); + Assert.AreEqual("Shilton", agent.Cust_Name); + Assert.AreEqual(200104, agent.Ord_Num); + Assert.AreEqual((decimal)1500.00, agent.Ord_Amount); + Assert.AreEqual((decimal)500.00, agent.Advance_Amount); + Assert.AreEqual("SOD", agent.Ord_Description); + // Non Existent Query Data + Assert.IsNull(agent.Agent); + Assert.IsNull(agent.Agent_Location); + Assert.IsNull(agent.Customer); + Assert.IsNull(agent.Customer_Location); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Entity_SelectQuery_Aliases() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql_Alias; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(34, data.Count); + var agent = data.FirstOrDefault(X => X.Agent_Code == "A004" && X.Cust_Code == "C00006"); + Assert.AreEqual("A004", agent.Agent_Code); + Assert.AreEqual("Ivan", agent.Agent); + Assert.AreEqual("Torento", agent.Agent_Location); + Assert.AreEqual("C00006", agent.Cust_Code); + Assert.AreEqual("Shilton", agent.Customer); + Assert.AreEqual("Torento", agent.Customer_Location); + // Non Existent Query Data + Assert.IsNull(agent.Agent_Name); + Assert.IsNull(agent.Cust_Name); + Assert.AreEqual(0, agent.Ord_Num); + Assert.AreEqual(0, agent.Ord_Amount); + Assert.AreEqual(0, agent.Advance_Amount); + Assert.IsNull(agent.Ord_Description); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Entity_DataTypes_Check() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql_DataTypes; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql); + Assert.AreEqual(1, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual(9223372036854775807, dataType.BigInt_Column); + Assert.IsTrue(dataType.Boolean_Column); + Assert.AreEqual("3q2+7w==", ConvertByteArrayToBase64(dataType.Bytea_Column)); + Assert.AreEqual("char10", dataType.Char_Column); + Assert.AreEqual("varchar50", dataType.Varchar_Column); + Assert.AreEqual("09/21/2024 00:00:00", ConvertToUSFormat(dataType.Date_Column.ToString())); + Assert.AreEqual(123456.789, dataType.Double_Column); + Assert.AreEqual(2147483647, dataType.Int_Column); + Assert.AreEqual((decimal)12345.67, dataType.Money_Column); + Assert.AreEqual((decimal)123456.789, dataType.Numeric_Column); + Assert.AreEqual((float)12345.67, dataType.Real_Column); + Assert.AreEqual(32767, dataType.SmallInt_Column); + Assert.AreEqual("some text", dataType.Text_Column); + Assert.AreEqual("12:34:56", dataType.Time_Column.ToString()); + Assert.AreEqual("09/21/2024 14:34:56", ConvertToUSFormat(dataType.Timestamp_Column.ToString())); + Assert.AreEqual("123e4567-e89b-12d3-a456-426614174000", dataType.Uuid_Column.ToString()); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Entity_Strict_Check() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql_Strict; + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql, strict: true); + Assert.AreEqual(34, data.Count); + var dataType = data.FirstOrDefault(); + Assert.AreEqual("A003", dataType.Agent_Code); + Assert.AreEqual("Alex", dataType.Agent); + Assert.AreEqual("C00013", dataType.Cust_Code); + Assert.AreEqual("Holmes", dataType.Customer); + Assert.AreEqual(200100, dataType.Ord_Num); + Assert.AreEqual((decimal)1000.00, dataType.Ord_Amount); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_FetchDataAsync_Entity_Strict_Error_Check() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.SelectSql_Strict; + try + { + var data = await new DBContext(DB.PostgreSQL, PostgreSQLConnectionString).FetchDataAsync(selectSql, strict: true); + Assert.Fail("No Exception"); } catch (IndexOutOfRangeException ex) { @@ -284,7 +540,7 @@ public void Test_PostgreSQL_FetchData_Entity_Strict_Error_Check() #endregion - #region Execute Scalar Tests - << string ExecuteScalar(string sqlStatement); >> + #region Execute Scalar Tests - << string ExecuteScalar(string sqlStatement) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] @@ -370,7 +626,93 @@ public void Test_PostgreSQL_ExecuteScalar_As_StringReturn_UnsupportedCommands() #endregion - #region Execute Scalar Tests - << T ExecuteScalar(string sqlStatement); >> + #region Execute Scalar Async Tests - << Task ExecuteScalarAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteScalarAsync_As_StringReturn_Scalar_Queries() + { + var countOfRecords = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Count_Of_Records; + var max = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Max; + var min = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Min; + var sum = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Sum; + var avg = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Avg; + var singleValueSelect = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Single_Value_Select; + + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + var count = await dbContext.ExecuteScalarAsync(countOfRecords); + Assert.AreEqual("12", count); + var maxValue = await dbContext.ExecuteScalarAsync(max); + Assert.AreEqual("10000.00", maxValue); + var minValue = await dbContext.ExecuteScalarAsync(min); + Assert.AreEqual("3000.00", minValue); + var sumValue = await dbContext.ExecuteScalarAsync(sum); + Assert.AreEqual("161000.00", sumValue); + var avgValue = await dbContext.ExecuteScalarAsync(avg); + Assert.AreEqual("6520.0000000000000000", avgValue); + var singleValue = await dbContext.ExecuteScalarAsync(singleValueSelect); + Assert.AreEqual("2", singleValue); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteScalarAsync_As_StringReturn_DefaultValue() + { + var noValueReturned = Queries.PostgreSQLQueries.TestDB.ScalarQueries.No_Value_Returned; + var dBNullValue = Queries.PostgreSQLQueries.TestDB.ScalarQueries.DB_Null_Value; + + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + var result = await dbContext.ExecuteScalarAsync(noValueReturned); + Assert.IsInstanceOfType(result); + Assert.AreEqual("", result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(string.Empty, result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteScalarAsync_As_StringReturn_UnsupportedCommands() + { + var sqlStatements = new List + { + Queries.PostgreSQLQueries.TestDB.DDL.Create_Table, + Queries.PostgreSQLQueries.TestDB.DDL.Alter_Table, + Queries.PostgreSQLQueries.TestDB.DDL.Comment_Table, + Queries.PostgreSQLQueries.TestDB.DDL.Truncate_Table, + Queries.PostgreSQLQueries.TestDB.DDL.Drop_Table, + + Queries.PostgreSQLQueries.TestDB.DML.InsertSql, + Queries.PostgreSQLQueries.TestDB.DML.UpdateSql, + Queries.PostgreSQLQueries.TestDB.DML.DeleteSql, + + Queries.PostgreSQLQueries.TestDB.DCL.GrantSql_Command_Table_User, + Queries.PostgreSQLQueries.TestDB.DCL.RevokeSql_Command_Table_User + }; + + foreach (var sqlStatement in sqlStatements) + { + try + { + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + await dbContext.ExecuteScalarAsync(sqlStatement); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("Only SELECT queries are supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteScalar' only supports SELECT queries that have a scalar (single value) return.", ex.AdditionalInfo); + } + } + } + + #endregion + + #region Execute Scalar Tests - << T ExecuteScalar(string sqlStatement) >> [TestMethod] [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] @@ -415,90 +757,255 @@ public void Test_PostgreSQL_ExecuteScalar_As_TypedReturn_DefaultValue() Assert.IsInstanceOfType(result); Assert.AreEqual(default(long), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(short), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(uint), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(ulong), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(ushort), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(decimal), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(double), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(float), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(byte), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(bool), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(DateTime), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(Guid), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(string), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(int?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(long?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(short?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(decimal?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(double?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(float?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(bool?), result); + + result = dbContext.ExecuteScalar(dBNullValue); + Assert.IsNull(result); + Assert.AreEqual(default(DateTime?), result); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public void Test_PostgreSQL_ExecuteScalar_As_TypedReturn_UnsupportedCommands() + { + var sqlStatements = new List + { + Queries.PostgreSQLQueries.TestDB.DDL.Create_Table, + Queries.PostgreSQLQueries.TestDB.DDL.Alter_Table, + Queries.PostgreSQLQueries.TestDB.DDL.Comment_Table, + Queries.PostgreSQLQueries.TestDB.DDL.Truncate_Table, + Queries.PostgreSQLQueries.TestDB.DDL.Drop_Table, + + Queries.PostgreSQLQueries.TestDB.DML.InsertSql, + Queries.PostgreSQLQueries.TestDB.DML.UpdateSql, + Queries.PostgreSQLQueries.TestDB.DML.DeleteSql, + + Queries.PostgreSQLQueries.TestDB.DCL.GrantSql_Command_Table_User, + Queries.PostgreSQLQueries.TestDB.DCL.RevokeSql_Command_Table_User + }; + + foreach (var sqlStatement in sqlStatements) + { + try + { + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + dbContext.ExecuteScalar(sqlStatement); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("Only SELECT queries are supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteScalar' only supports SELECT queries that have a scalar (single value) return.", ex.AdditionalInfo); + } + } + } + + #endregion + + #region Execute Scalar Async Tests - << Task ExecuteScalarAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteScalarAsync_As_TypedReturn_Scalar_Queries() + { + var countOfRecords = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Count_Of_Records; + var max = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Max; + var min = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Min; + var sum = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Sum; + var avg = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Avg; + var singleValueSelect = Queries.PostgreSQLQueries.TestDB.ScalarQueries.Single_Value_Select; + + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + var count = await dbContext.ExecuteScalarAsync(countOfRecords); + Assert.AreEqual(12, count); + var maxValue = await dbContext.ExecuteScalarAsync(max); + Assert.AreEqual(10000.00, maxValue); + var minValue = await dbContext.ExecuteScalarAsync(min); + Assert.AreEqual(3000.00, minValue); + var sumValue = await dbContext.ExecuteScalarAsync(sum); + Assert.AreEqual(161000.00, sumValue); + var avgValue = await dbContext.ExecuteScalarAsync(avg); + Assert.AreEqual((decimal)6520.0000000000000000, avgValue); + var singleValue = await dbContext.ExecuteScalarAsync(singleValueSelect); + Assert.AreEqual("2", singleValue); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteScalarAsync_As_TypedReturn_DefaultValue() + { + var dBNullValue = Queries.PostgreSQLQueries.TestDB.ScalarQueries.DB_Null_Value; + + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + dynamic result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(int), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); + Assert.IsInstanceOfType(result); + Assert.AreEqual(default(long), result); + + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(short), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(uint), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(ulong), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(ushort), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(decimal), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(double), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(float), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(byte), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(bool), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(DateTime), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsInstanceOfType(result); Assert.AreEqual(default(Guid), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(string), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(int?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(long?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(short?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(decimal?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(double?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(float?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(bool?), result); - result = dbContext.ExecuteScalar(dBNullValue); + result = await dbContext.ExecuteScalarAsync(dBNullValue); Assert.IsNull(result); Assert.AreEqual(default(DateTime?), result); } [TestMethod] [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] - public void Test_PostgreSQL_ExecuteScalar_As_TypedReturn_UnsupportedCommands() + public async Task Test_PostgreSQL_ExecuteScalarAsync_As_TypedReturn_UnsupportedCommands() { var sqlStatements = new List { @@ -521,7 +1028,7 @@ public void Test_PostgreSQL_ExecuteScalar_As_TypedReturn_UnsupportedCommands() try { var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); - dbContext.ExecuteScalar(sqlStatement); + await dbContext.ExecuteScalarAsync(sqlStatement); Assert.Fail("No Exception"); } catch (QueryDBException ex) @@ -698,6 +1205,169 @@ public void Test_PostgreSQL_ExecuteCommand_DCL_Queries() #endregion + #region Execute Command Async Tests - << Task ExecuteCommandAsync(string sqlStatement) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteCommandAsync_DDL_Queries() + { + var createTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Alter_Table; + var commentTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Comment_Table; + var commentTableColumnSql = Queries.PostgreSQLQueries.TestDB.DDL.Comment_Table_Column; + var truncateTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.PostgreSQLQueries.TestDB.DDL.DDL_Execute_check; + var dDLTableCommentCheckSql = Queries.PostgreSQLQueries.TestDB.DDL.DDL_Table_Comment_check; + var dDLTableColumnCommentCheckSql = Queries.PostgreSQLQueries.TestDB.DDL.DDL_Table_Column_Comment_check; + + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + await dbContext.ExecuteCommandAsync(createTableSql); + await dbContext.ExecuteCommandAsync(alterTableSql); + await dbContext.ExecuteCommandAsync(commentTableSql); + await dbContext.ExecuteCommandAsync(commentTableColumnSql); + await dbContext.ExecuteCommandAsync(truncateTableSql); + + var tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "public", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["table_count"]); + var tableComment = await dbContext + .FetchDataAsync(string.Format(dDLTableCommentCheckSql, "public", "Employee")); + Assert.AreEqual("This table stores employee records", tableComment[0].ReferenceData["table_comment"]); + var tableColumnComment = await dbContext + .FetchDataAsync(string.Format(dDLTableColumnCommentCheckSql, "public", "Employee")); + Assert.AreEqual("This column stores employee middle name", tableColumnComment[3].ReferenceData["table_column_comment"]); + + await dbContext.ExecuteCommandAsync(renameTableSql); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "public", "Employee")); + Assert.AreEqual("0", tableCount[0].ReferenceData["table_count"]); + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "public", "Employees")); + Assert.AreEqual("1", tableCount[0].ReferenceData["table_count"]); + + await dbContext.ExecuteCommandAsync(dropTableSql); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "public", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["table_count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteCommandAsync_DML_Queries() + { + var insertSql = Queries.PostgreSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.PostgreSQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.PostgreSQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.PostgreSQLQueries.TestDB.DML.VerifyDMLExecution; + + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + // Insert + var rows = await dbContext.ExecuteCommandAsync(insertSql); + Assert.AreEqual(1, rows); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["agent_code"]); + Assert.AreEqual("John", agent.ReferenceData["agent_name"]); + Assert.AreEqual("Wick", agent.ReferenceData["working_area"]); + Assert.AreEqual("0.11", agent.ReferenceData["commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["phone_no"]); + Assert.AreEqual("", agent.ReferenceData["country"]); + + // Update + rows = await dbContext.ExecuteCommandAsync(updateSql); + Assert.AreEqual(1, rows); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["agent_code"]); + Assert.AreEqual("John", agent.ReferenceData["agent_name"]); + Assert.AreEqual("Wick", agent.ReferenceData["working_area"]); + Assert.AreEqual("0.15", agent.ReferenceData["commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["phone_no"]); + Assert.AreEqual("", agent.ReferenceData["country"]); + + // Delete + rows = await dbContext.ExecuteCommandAsync(deleteSql); + Assert.AreEqual(1, rows); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteCommandAsync_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + var rows = await dbContext.ExecuteCommandAsync(selectSql); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteCommand' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteCommandAsync_DCL_Queries() + { + var user = "test_user"; + var password = "Test@1234"; + var table = "Agents"; + var commands = "SELECT, UPDATE"; + var checkCommand = "SELECT"; + + var createUser = string.Format(Queries.PostgreSQLQueries.TestDB.DCL.CreateUserSql_User_Password, user, password); + var grantSql = string.Format(Queries.PostgreSQLQueries.TestDB.DCL.GrantSql_Command_Table_User, commands, table, user); + var revokeSql = string.Format(Queries.PostgreSQLQueries.TestDB.DCL.RevokeSql_Command_Table_User, commands, table, user); + var verifyPermissions = string.Format(Queries.PostgreSQLQueries.TestDB.DCL.VerifyPermission_User, user); + var removeUser = string.Format(Queries.PostgreSQLQueries.TestDB.DCL.RemoveUserSql_User, user); + + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + // Create User + var result = await dbContext.ExecuteCommandAsync(createUser); + Assert.AreEqual(-1, result); + + // Existing Permissions + var data = await dbContext.FetchDataAsync(verifyPermissions); + Assert.AreEqual(0, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Grant + result = await dbContext.ExecuteCommandAsync(grantSql); + Assert.AreEqual(-1, result); + data = await dbContext.FetchDataAsync(verifyPermissions); + Assert.AreEqual(2, data.Count); + Assert.IsTrue(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Revoke + result = await dbContext.ExecuteCommandAsync(revokeSql); + Assert.AreEqual(-1, result); + data = await dbContext.FetchDataAsync(verifyPermissions); + Assert.AreEqual(0, data.Count); + Assert.IsFalse(data.Any(data => data.ReferenceData.Values.Any(value => value.Contains(checkCommand)))); + + // Remove User + result = await dbContext.ExecuteCommandAsync(removeUser); + Assert.AreEqual(-1, result); + } + + #endregion + #region Execute Transaction Tests - << bool ExecuteTransaction(List sqlStatements) >> [TestMethod] @@ -720,7 +1390,7 @@ public void Test_PostgreSQL_ExecuteTransaction_DDL_Multiple_Queries() }; var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); var result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); var tableCount = dbContext .FetchData(string.Format(dDLExecutionCheckSql, "public", "Employee")); @@ -733,7 +1403,7 @@ public void Test_PostgreSQL_ExecuteTransaction_DDL_Multiple_Queries() dropTableSql }; result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); tableCount = dbContext .FetchData(string.Format(dDLExecutionCheckSql, "public", "Employees")); @@ -758,7 +1428,7 @@ public void Test_PostgreSQL_ExecuteTransaction_DML_Multiple_Queries() // Insert & Update var result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); var data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(1, data.Count); var agent = data.FirstOrDefault(); @@ -775,7 +1445,7 @@ public void Test_PostgreSQL_ExecuteTransaction_DML_Multiple_Queries() deleteSql }; result = dbContext.ExecuteTransaction(statements); - Assert.IsTrue(result); + Assert.IsTrue(result.Success); data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(0, data.Count); } @@ -799,7 +1469,8 @@ public void Test_PostgreSQL_ExecuteTransaction_Incomplete_Transaction_Rollback_O // Insert & Update var result = dbContext.ExecuteTransaction(statements); - Assert.IsFalse(result); + Assert.IsFalse(result.Success); + Assert.IsTrue(result.Exception.Message.Contains("42601: syntax error at end of input")); var data = dbContext.FetchData(verifyDMLExecution); Assert.AreEqual(0, data.Count); } @@ -831,6 +1502,140 @@ public void Test_PostgreSQL_ExecuteTransaction_DML_Unsupported_SELECT_Queries() #endregion + #region Execute Transaction Async Tests - << Task ExecuteTransactionAsync(List sqlStatements) >> + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteTransactionAsync_DDL_Multiple_Queries() + { + var createTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Create_Table; + var alterTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Alter_Table; + var truncateTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Truncate_Table; + var renameTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Rename_Table; + var dropTableSql = Queries.PostgreSQLQueries.TestDB.DDL.Drop_Table; + var dDLExecutionCheckSql = Queries.PostgreSQLQueries.TestDB.DDL.DDL_Execute_check; + + // Create, Alter & Truncate + var statements = new List + { + createTableSql, + alterTableSql, + truncateTableSql + }; + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + + var tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "public", "Employee")); + Assert.AreEqual("1", tableCount[0].ReferenceData["table_count"]); + + // Rename & Drop + statements = new List + { + renameTableSql, + dropTableSql + }; + result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + + tableCount = await dbContext + .FetchDataAsync(string.Format(dDLExecutionCheckSql, "public", "Employees")); + Assert.AreEqual("0", tableCount[0].ReferenceData["table_count"]); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteTransactionAsync_DML_Multiple_Queries() + { + var insertSql = Queries.PostgreSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.PostgreSQLQueries.TestDB.DML.UpdateSql; + var deleteSql = Queries.PostgreSQLQueries.TestDB.DML.DeleteSql; + var verifyDMLExecution = Queries.PostgreSQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql + }; + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + // Insert & Update + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(1, data.Count); + var agent = data.FirstOrDefault(); + Assert.AreEqual("A020", agent.ReferenceData["agent_code"]); + Assert.AreEqual("John", agent.ReferenceData["agent_name"]); + Assert.AreEqual("Wick", agent.ReferenceData["working_area"]); + Assert.AreEqual("0.15", agent.ReferenceData["commission"]); + Assert.AreEqual("010-44536178", agent.ReferenceData["phone_no"]); + Assert.AreEqual("", agent.ReferenceData["country"]); + + // Delete + statements = new List + { + deleteSql + }; + result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsTrue(result.Success); + data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteTransactionAsync_Incomplete_Transaction_Rollback_On_Error() + { + var insertSql = Queries.PostgreSQLQueries.TestDB.DML.InsertSql; + var updateSql = Queries.PostgreSQLQueries.TestDB.DML.UpdateSql; + var updateErrorSql = "UPDATE"; + var verifyDMLExecution = Queries.PostgreSQLQueries.TestDB.DML.VerifyDMLExecution; + + var statements = new List + { + insertSql, + updateSql, + updateErrorSql + }; + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + + // Insert & Update + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.IsFalse(result.Success); + Assert.IsTrue(result.Exception.Message.Contains("42601: syntax error at end of input")); + var data = await dbContext.FetchDataAsync(verifyDMLExecution); + Assert.AreEqual(0, data.Count); + } + + [TestMethod] + [TestCategory(DB_TESTS), TestCategory(POSTGRESQL_TESTS)] + public async Task Test_PostgreSQL_ExecuteTransactionAsync_DML_Unsupported_SELECT_Queries() + { + var selectSql = Queries.PostgreSQLQueries.TestDB.DML.SelectSql; + + // Select + try + { + var statements = new List + { + selectSql + }; + var dbContext = new DBContext(DB.PostgreSQL, PostgreSQLConnectionString); + var result = await dbContext.ExecuteTransactionAsync(statements); + Assert.Fail("No Exception"); + } + catch (QueryDBException ex) + { + Assert.AreEqual("SELECT queries are not supported here.", ex.Message); + Assert.AreEqual("UnsupportedCommand", ex.ErrorType); + Assert.AreEqual("'ExecuteTransaction' doesn't support SELECT queries.", ex.AdditionalInfo); + } + } + + #endregion + #endregion } diff --git a/QueryDB.Core.Tests/QueryDBTests.cs b/QueryDB.Core.Tests/QueryDBTests.cs index c847e8e..95f2d8a 100644 --- a/QueryDB.Core.Tests/QueryDBTests.cs +++ b/QueryDB.Core.Tests/QueryDBTests.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; +using System.Threading.Tasks; namespace QueryDB.Core.Tests { @@ -23,17 +24,14 @@ public void ExecuteCommand_UnknownDB_ReturnsNegativeOne() [TestMethod] [TestCategory(DB_TESTS), TestCategory(UNKNOW_DB_TESTS)] - public void ExecuteTransaction_UnknownDB_ReturnsFalse() + public async Task ExecuteCommandAsync_UnknownDB_ReturnsNegativeOne() { - var sqlStatements = new List - { - "DELETE FROM users" - }; + string sqlStatement = "DELETE FROM users"; var dbContext = new DBContext((DB)999, "some_invalid_connection_string"); - var result = dbContext.ExecuteTransaction(sqlStatements); + var result = await dbContext.ExecuteCommandAsync(sqlStatement); - Assert.IsFalse(result); + Assert.AreEqual(-1, result); } #endregion diff --git a/QueryDB/DBContext.cs b/QueryDB/DBContext.cs index fbc45d0..cc879fd 100644 --- a/QueryDB/DBContext.cs +++ b/QueryDB/DBContext.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using System.Threading.Tasks; namespace QueryDB { @@ -56,13 +57,13 @@ public DBContext(DB database, string connectionString) } /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// Converts column names to keys holding values, with multiple database rows returned into a list. /// Note: Use aliases in query for similar column names. /// /// 'Select' query. - /// Boolean parameter to return dictionary keys in uppercase. Default - 'false'. - /// List of data Dictionary with column names as keys holding values into a list for multiple rows of data. + /// Boolean parameter to return dictionary keys in uppercase. Default - false. + /// List of with column names as keys holding values into a list for multiple rows of data. public List FetchData(string selectSql, bool upperCaseKeys = false) { var dataList = new List(); @@ -102,12 +103,12 @@ public List FetchData(string selectSql, bool upperCaseKeys = fal } /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// /// Object entity to return data mapped into. /// 'Select' query. - /// Enables fetch data only for object properties existing in database query result. Default - 'false'. - /// List of data rows mapped into object entity into a list for multiple rows of data. + /// Enables fetch data only for object type properties existing in database query result. Default - false. + /// List of data rows mapped into object of type . public List FetchData(string selectSql, bool strict = false) where T : new() { var dataList = new List(); @@ -147,10 +148,105 @@ public List FetchData(string selectSql, bool upperCaseKeys = fal } /// - /// Executes a SQL query and returns the result as a string. + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// Converts column names to keys holding values, with multiple database rows returned into a list. + /// Note: Use aliases in query for similar column names. /// - /// The SQL query to execute. - /// A string representing the result of the query. If the result is DBNull, an empty string is returned. + /// 'Select' query. + /// Boolean parameter to return dictionary keys in uppercase. Default - false. + /// List of with column names as keys holding values into a list for multiple rows of data. + public async Task> FetchDataAsync(string selectSql, bool upperCaseKeys = false) + { + var dataList = new List(); + if (Database.Equals(DB.MSSQL)) + { + using (var msSqlDBConnection = GetSqlServerConnection()) + { + var _systemAdapter = new MSSQL.Adapter(); + dataList = await _systemAdapter.FetchDataAsync(selectSql, msSqlDBConnection.SqlConnection, upperCaseKeys); + } + } + else if (Database.Equals(DB.MySQL)) + { + using (var mySqlDBConnection = GetMySqlConnection()) + { + var _systemAdapter = new MySQL.Adapter(); + dataList = await _systemAdapter.FetchDataAsync(selectSql, mySqlDBConnection.MySqlConnection, upperCaseKeys); + } + } + else if (Database.Equals(DB.Oracle)) + { + using (var oracleDBConnection = GetOracleConnection()) + { + var _systemAdapter = new Oracle.Adapter(); + dataList = await _systemAdapter.FetchDataAsync(selectSql, oracleDBConnection.OracleConnection, upperCaseKeys); + } + } + else if (Database.Equals(DB.PostgreSQL)) + { + using (var postgreSqlDBConnection = GetPostgreSqlConnection()) + { + var _systemAdapter = new PostgreSQL.Adapter(); + dataList = await _systemAdapter.FetchDataAsync(selectSql, postgreSqlDBConnection.PostgreSQLConnection, upperCaseKeys); + } + } + return dataList; + } + + /// + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// + /// Object entity to return data mapped into. + /// 'Select' query. + /// Enables fetch data only for object type properties existing in database query result. Default - false. + /// List of data rows mapped into object of type . + public async Task> FetchDataAsync(string selectSql, bool strict = false) where T : new() + { + var dataList = new List(); + if (Database.Equals(DB.MSSQL)) + { + using (var msSqlDBConnection = GetSqlServerConnection()) + { + var _systemAdapter = new MSSQL.Adapter(); + dataList = await _systemAdapter.FetchDataAsync(selectSql, msSqlDBConnection.SqlConnection, strict); + } + } + else if (Database.Equals(DB.MySQL)) + { + using (var mySqlDBConnection = GetMySqlConnection()) + { + var _systemAdapter = new MySQL.Adapter(); + dataList = await _systemAdapter.FetchDataAsync(selectSql, mySqlDBConnection.MySqlConnection, strict); + } + } + else if (Database.Equals(DB.Oracle)) + { + using (var oracleDBConnection = GetOracleConnection()) + { + var _systemAdapter = new Oracle.Adapter(); + dataList = await _systemAdapter.FetchDataAsync(selectSql, oracleDBConnection.OracleConnection, strict); + } + } + else if (Database.Equals(DB.PostgreSQL)) + { + using (var postgreSqlDBConnection = GetPostgreSqlConnection()) + { + var _systemAdapter = new PostgreSQL.Adapter(); + dataList = await _systemAdapter.FetchDataAsync(selectSql, postgreSqlDBConnection.PostgreSQLConnection, strict); + } + } + return dataList; + } + + /// + /// Executes the provided SQL statement and returns the first column of the first row in the result set. + /// If the result is DBNull, an empty string is returned. + /// + /// The SQL statement to execute. It should be a query that returns a single value. + /// + /// A representing the value of the first column of the first row in the result set, + /// or an empty string if the result is DBNull. + /// public string ExecuteScalar(string sqlStatement) { if (!Regex.IsMatch(sqlStatement, Utils.SelectQueryPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromSeconds(5))) @@ -193,11 +289,15 @@ public string ExecuteScalar(string sqlStatement) } /// - /// Executes a SQL query and returns the result as the specified type. + /// Executes the provided SQL statement and returns the first column of the first row in the result set, + /// converted to the specified type . If the result is DBNull, the default value of is returned. /// /// The type to which the result should be converted. - /// The SQL query to execute. - /// The result of the query, converted to the specified type. If the result is DBNull, the default value for the type is returned. + /// The SQL statement to execute. It should be a query that returns a single value. + /// + /// The value of the first column of the first row in the result set, converted to type , + /// or the default value of if the result is DBNull. + /// public T ExecuteScalar(string sqlStatement) { if (!Regex.IsMatch(sqlStatement, Utils.SelectQueryPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromSeconds(5))) @@ -240,10 +340,111 @@ public T ExecuteScalar(string sqlStatement) } /// - /// Executes SQL commands. + /// Asynchronously executes the provided SQL statement and returns the first column of the first row in the result set. + /// If the result is DBNull, an empty string is returned. /// - /// SQL statement as command. - /// The number of rows affected. + /// The SQL statement to execute. It should be a query that returns a single value. + /// + /// A representing the value of the first column of the first row in the result set, + /// or an empty string if the result is DBNull. + /// + public async Task ExecuteScalarAsync(string sqlStatement) + { + if (!Regex.IsMatch(sqlStatement, Utils.SelectQueryPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromSeconds(5))) + throw new QueryDBException(QueryDBExceptions.ErrorMessage.UnsupportedExecuteScalarCommand, + QueryDBExceptions.ErrorType.UnsupportedCommand, QueryDBExceptions.AdditionalInfo.UnsupportedExecuteScalarCommand); + var value = string.Empty; + if (Database.Equals(DB.MSSQL)) + { + using (var msSqlDBConnection = GetSqlServerConnection()) + { + var _systemAdapter = new MSSQL.Adapter(); + value = await _systemAdapter.ExecuteScalarAsync(sqlStatement, msSqlDBConnection.SqlConnection); + } + } + else if (Database.Equals(DB.MySQL)) + { + using (var mySqlDBConnection = GetMySqlConnection()) + { + var _systemAdapter = new MySQL.Adapter(); + value = await _systemAdapter.ExecuteScalarAsync(sqlStatement, mySqlDBConnection.MySqlConnection); + } + } + else if (Database.Equals(DB.Oracle)) + { + using (var oracleDBConnection = GetOracleConnection()) + { + var _systemAdapter = new Oracle.Adapter(); + value = await _systemAdapter.ExecuteScalarAsync(sqlStatement, oracleDBConnection.OracleConnection); + } + } + else if (Database.Equals(DB.PostgreSQL)) + { + using (var postgreSqlDBConnection = GetPostgreSqlConnection()) + { + var _systemAdapter = new PostgreSQL.Adapter(); + value = await _systemAdapter.ExecuteScalarAsync(sqlStatement, postgreSqlDBConnection.PostgreSQLConnection); + } + } + return value; + } + + /// + /// Asynchronously executes the provided SQL statement and returns the first column of the first row in the result set, + /// converted to the specified type . If the result is DBNull, the default value of is returned. + /// + /// The type to which the result should be converted. + /// The SQL statement to execute. It should be a query that returns a single value. + /// + /// The value of the first column of the first row in the result set, converted to type , + /// or the default value of if the result is DBNull. + /// + public async Task ExecuteScalarAsync(string sqlStatement) + { + if (!Regex.IsMatch(sqlStatement, Utils.SelectQueryPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromSeconds(5))) + throw new QueryDBException(QueryDBExceptions.ErrorMessage.UnsupportedExecuteScalarCommand, + QueryDBExceptions.ErrorType.UnsupportedCommand, QueryDBExceptions.AdditionalInfo.UnsupportedExecuteScalarCommand); + var value = default(T); + if (Database.Equals(DB.MSSQL)) + { + using (var msSqlDBConnection = GetSqlServerConnection()) + { + var _systemAdapter = new MSSQL.Adapter(); + value = await _systemAdapter.ExecuteScalarAsync(sqlStatement, msSqlDBConnection.SqlConnection); + } + } + else if (Database.Equals(DB.MySQL)) + { + using (var mySqlDBConnection = GetMySqlConnection()) + { + var _systemAdapter = new MySQL.Adapter(); + value = await _systemAdapter.ExecuteScalarAsync(sqlStatement, mySqlDBConnection.MySqlConnection); + } + } + else if (Database.Equals(DB.Oracle)) + { + using (var oracleDBConnection = GetOracleConnection()) + { + var _systemAdapter = new Oracle.Adapter(); + value = await _systemAdapter.ExecuteScalarAsync(sqlStatement, oracleDBConnection.OracleConnection); + } + } + else if (Database.Equals(DB.PostgreSQL)) + { + using (var postgreSqlDBConnection = GetPostgreSqlConnection()) + { + var _systemAdapter = new PostgreSQL.Adapter(); + value = await _systemAdapter.ExecuteScalarAsync(sqlStatement, postgreSqlDBConnection.PostgreSQLConnection); + } + } + return value; + } + + /// + /// Executes a SQL statement that does not return a result set. + /// + /// SQL statement to execute. + /// The number of rows affected by the execution of the SQL statement. public int ExecuteCommand(string sqlStatement) { if (Regex.IsMatch(sqlStatement, Utils.SelectQueryPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromSeconds(5))) @@ -284,16 +485,112 @@ public int ExecuteCommand(string sqlStatement) return -1; } + /// + /// Asynchronously executes a SQL statement that does not return a result set. + /// + /// SQL statement to execute. + /// The number of rows affected by the execution of the SQL statement. + public async Task ExecuteCommandAsync(string sqlStatement) + { + if (Regex.IsMatch(sqlStatement, Utils.SelectQueryPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromSeconds(5))) + throw new QueryDBException(QueryDBExceptions.ErrorMessage.UnsupportedSelectExecuteCommand, + QueryDBExceptions.ErrorType.UnsupportedCommand, QueryDBExceptions.AdditionalInfo.UnsupportedSelectExecuteCommand); + if (Database.Equals(DB.MSSQL)) + { + using (var msSqlDBConnection = GetSqlServerConnection()) + { + var _systemAdapter = new MSSQL.Adapter(); + return await _systemAdapter.ExecuteCommandAsync(sqlStatement, msSqlDBConnection.SqlConnection); + } + } + else if (Database.Equals(DB.MySQL)) + { + using (var mySqlDBConnection = GetMySqlConnection()) + { + var _systemAdapter = new MySQL.Adapter(); + return await _systemAdapter.ExecuteCommandAsync(sqlStatement, mySqlDBConnection.MySqlConnection); + } + } + else if (Database.Equals(DB.Oracle)) + { + using (var oracleDBConnection = GetOracleConnection()) + { + var _systemAdapter = new Oracle.Adapter(); + return await _systemAdapter.ExecuteCommandAsync(sqlStatement, oracleDBConnection.OracleConnection); + } + } + else if (Database.Equals(DB.PostgreSQL)) + { + using (var postgreSqlDBConnection = GetPostgreSqlConnection()) + { + var _systemAdapter = new PostgreSQL.Adapter(); + return await _systemAdapter.ExecuteCommandAsync(sqlStatement, postgreSqlDBConnection.PostgreSQLConnection); + } + } + return -1; + } + /// /// Executes multiple SQL statements within a transaction, ensuring that all statements are executed together. /// /// A list of SQL statements to execute. /// - /// Returns true if all statements are executed successfully and the transaction is committed; - /// false if any statement fails and the transaction is rolled back. + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. + /// + public Result ExecuteTransaction(List sqlStatements) + { + var result = new Result(); + var selectExists = sqlStatements.Any(sqlStatement => Regex.IsMatch(sqlStatement, Utils.SelectQueryPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromSeconds(5))); + if (selectExists) + throw new QueryDBException(QueryDBExceptions.ErrorMessage.UnsupportedSelectExecuteTransaction, + QueryDBExceptions.ErrorType.UnsupportedCommand, QueryDBExceptions.AdditionalInfo.UnsupportedSelectExecuteTransaction); + if (Database.Equals(DB.MSSQL)) + { + using (var msSqlDBConnection = GetSqlServerConnection()) + { + result = MSSQL.Adapter.ExecuteTransaction(sqlStatements, msSqlDBConnection.SqlConnection); + } + } + else if (Database.Equals(DB.MySQL)) + { + using (var mySqlDBConnection = GetMySqlConnection()) + { + result = MySQL.Adapter.ExecuteTransaction(sqlStatements, mySqlDBConnection.MySqlConnection); + } + } + else if (Database.Equals(DB.Oracle)) + { + using (var oracleDBConnection = GetOracleConnection()) + { + result = Oracle.Adapter.ExecuteTransaction(sqlStatements, oracleDBConnection.OracleConnection); + } + } + else if (Database.Equals(DB.PostgreSQL)) + { + using (var postgreSqlDBConnection = GetPostgreSqlConnection()) + { + result = PostgreSQL.Adapter.ExecuteTransaction(sqlStatements, postgreSqlDBConnection.PostgreSQLConnection); + } + } + return result; + } + + /// + /// Asynchronously executes multiple SQL statements within a transaction, ensuring that all statements are executed together. + /// + /// A list of SQL statements to execute. + /// + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. /// - public bool ExecuteTransaction(List sqlStatements) + public async Task ExecuteTransactionAsync(List sqlStatements) { + var result = new Result(); var selectExists = sqlStatements.Any(sqlStatement => Regex.IsMatch(sqlStatement, Utils.SelectQueryPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline, TimeSpan.FromSeconds(5))); if (selectExists) throw new QueryDBException(QueryDBExceptions.ErrorMessage.UnsupportedSelectExecuteTransaction, @@ -302,31 +599,31 @@ public bool ExecuteTransaction(List sqlStatements) { using (var msSqlDBConnection = GetSqlServerConnection()) { - return MSSQL.Adapter.ExecuteTransaction(sqlStatements, msSqlDBConnection.SqlConnection); + result = await MSSQL.Adapter.ExecuteTransactionAsync(sqlStatements, msSqlDBConnection.SqlConnection); } } else if (Database.Equals(DB.MySQL)) { using (var mySqlDBConnection = GetMySqlConnection()) { - return MySQL.Adapter.ExecuteTransaction(sqlStatements, mySqlDBConnection.MySqlConnection); + result = await MySQL.Adapter.ExecuteTransactionAsync(sqlStatements, mySqlDBConnection.MySqlConnection); } } else if (Database.Equals(DB.Oracle)) { using (var oracleDBConnection = GetOracleConnection()) { - return Oracle.Adapter.ExecuteTransaction(sqlStatements, oracleDBConnection.OracleConnection); + result = await Oracle.Adapter.ExecuteTransactionAsync(sqlStatements, oracleDBConnection.OracleConnection); } } else if (Database.Equals(DB.PostgreSQL)) { using (var postgreSqlDBConnection = GetPostgreSqlConnection()) { - return PostgreSQL.Adapter.ExecuteTransaction(sqlStatements, postgreSqlDBConnection.PostgreSQLConnection); + result = await PostgreSQL.Adapter.ExecuteTransactionAsync(sqlStatements, postgreSqlDBConnection.PostgreSQLConnection); } } - return false; + return result; } /// diff --git a/QueryDB/IDBContext.cs b/QueryDB/IDBContext.cs index f14d694..786a856 100644 --- a/QueryDB/IDBContext.cs +++ b/QueryDB/IDBContext.cs @@ -1,5 +1,6 @@ using QueryDB.Resources; using System.Collections.Generic; +using System.Threading.Tasks; namespace QueryDB { @@ -9,52 +10,120 @@ namespace QueryDB interface IDBContext { /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// /// 'Select' query. - /// Boolean parameter to return dictionary keys in uppercase. Default - 'false'. - /// List of data Dictionary with column names as keys holding values into a list for multiple rows of data. + /// Boolean parameter to return dictionary keys in uppercase. Default - false. + /// List of with column names as keys holding values into a list for multiple rows of data. List FetchData(string selectSql, bool upperCaseKeys = false); /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// /// Object entity to return data mapped into. /// 'Select' query. - /// Enables fetch data only for object properties existing in database query result. Default - 'false'. - /// List of data rows mapped into object entity into a list for multiple rows of data. + /// Enables fetch data only for object type properties existing in database query result. Default - false. + /// List of data rows mapped into object of type . List FetchData(string selectSql, bool strict = false) where T : new(); /// - /// Executes a SQL query and returns the result as a string. + /// Asynchronously executes and retrieves records for 'Select' queries from the database. /// - /// The SQL query to execute. - /// A string representing the result of the query. If the result is DBNull, an empty string is returned. + /// 'Select' query. + /// Boolean parameter to return dictionary keys in uppercase. Default - false. + /// List of with column names as keys holding values into a list for multiple rows of data. + Task> FetchDataAsync(string selectSql, bool upperCaseKeys = false); + + /// + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// + /// Object entity to return data mapped into. + /// 'Select' query. + /// Enables fetch data only for object type properties existing in database query result. Default - false. + Task> FetchDataAsync(string selectSql, bool strict = false) where T : new(); + + /// + /// Executes the provided SQL statement and returns the first column of the first row in the result set. + /// If the result is DBNull, an empty string is returned. + /// + /// The SQL statement to execute. It should be a query that returns a single value. + /// + /// A representing the value of the first column of the first row in the result set, + /// or an empty string if the result is DBNull. + /// string ExecuteScalar(string sqlStatement); /// - /// Executes a SQL query and returns the result as the specified type. + /// Executes the provided SQL statement and returns the first column of the first row in the result set, + /// converted to the specified type . If the result is DBNull, the default value of is returned. /// /// The type to which the result should be converted. - /// The SQL query to execute. - /// The result of the query, converted to the specified type. If the result is DBNull, the default value for the type is returned. + /// The SQL statement to execute. It should be a query that returns a single value. + /// + /// The value of the first column of the first row in the result set, converted to type , + /// or the default value of if the result is DBNull. + /// T ExecuteScalar(string sqlStatement); /// - /// Executes SQL commands. + /// Asynchronously executes the provided SQL statement and returns the first column of the first row in the result set. + /// If the result is DBNull, an empty string is returned. /// - /// SQL statement as command. - /// The number of rows affected. + /// The SQL statement to execute. It should be a query that returns a single value. + /// + /// A representing the value of the first column of the first row in the result set, + /// or an empty string if the result is DBNull. + /// + Task ExecuteScalarAsync(string sqlStatement); + + /// + /// Asynchronously executes the provided SQL statement and returns the first column of the first row in the result set, + /// converted to the specified type . If the result is DBNull, the default value of is returned. + /// + /// The type to which the result should be converted. + /// The SQL statement to execute. It should be a query that returns a single value. + /// + /// The value of the first column of the first row in the result set, converted to type , + /// or the default value of if the result is DBNull. + /// + Task ExecuteScalarAsync(string sqlStatement); + + /// + /// Executes a SQL statement that does not return a result set. + /// + /// SQL statement to execute. + /// The number of rows affected by the execution of the SQL statement. int ExecuteCommand(string sqlStatement); + /// + /// Asynchronously executes a SQL statement that does not return a result set. + /// + /// SQL statement to execute. + /// The number of rows affected by the execution of the SQL statement. + Task ExecuteCommandAsync(string sqlStatement); + /// /// Executes multiple SQL statements within a transaction, ensuring that all statements are executed together. /// /// A list of SQL statements to execute. /// - /// Returns true if all statements are executed successfully and the transaction is committed; - /// false if any statement fails and the transaction is rolled back. + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. + /// + Result ExecuteTransaction(List sqlStatements); + + /// + /// Asynchronously executes multiple SQL statements within a transaction, ensuring that all statements are executed together. + /// + /// A list of SQL statements to execute. + /// + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. /// - bool ExecuteTransaction(List sqlStatements); + Task ExecuteTransactionAsync(List sqlStatements); } } diff --git a/QueryDB/MSSQL/Adapter.cs b/QueryDB/MSSQL/Adapter.cs index 4fd939d..8d1b025 100644 --- a/QueryDB/MSSQL/Adapter.cs +++ b/QueryDB/MSSQL/Adapter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Threading.Tasks; namespace QueryDB.MSSQL { @@ -11,13 +12,16 @@ namespace QueryDB.MSSQL /// internal class Adapter { + + #region Synchronous + /// - /// Gets the 'SQL Server' data reader. + /// Executes the specified SQL command and returns 'SQL Server' data reader. /// /// The text of the query. - /// 'SQL Server' connection. + /// The object used to connect to the database. /// Sql command type. - /// 'SQL Server' data reader. + /// A object that can be used to read the query results. internal SqlDataReader GetSqlReader(string cmdText, SqlConnection connection, CommandType commandType) { connection.Open(); @@ -32,7 +36,7 @@ internal SqlDataReader GetSqlReader(string cmdText, SqlConnection connection, Co /// connection, and command type. Opens the connection before creating the command. /// /// The SQL command text to execute. - /// The to use. + /// The object used to connect to the database. /// The type of the command (e.g., Text, StoredProcedure). /// A configured instance. internal SqlCommand GetSqlCommand(string cmdText, SqlConnection connection, CommandType commandType) @@ -58,7 +62,7 @@ internal static SqlCommand GetSqlCommand(string cmdText, SqlConnection connectio /// /// Initiates and returns a new SQL transaction for the specified database connection. /// - /// The SQL database connection. + /// The object used to connect to the database. /// A new associated with the provided connection. internal static SqlTransaction GetSqlTransaction(SqlConnection connection) { @@ -68,13 +72,14 @@ internal static SqlTransaction GetSqlTransaction(SqlConnection connection) } /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// Converts column names to keys holding values, with multiple database rows returned into a list. + /// Note: Use aliases in query for similar column names. /// /// 'Select' query. - /// 'Sql' Connection. + /// The object used to connect to the database. /// Boolean parameter to return dictionary keys in uppercase. - /// List of data Dictionary with column names as keys holding values into a list for multiple rows of data. + /// List of with column names as keys holding values into a list for multiple rows of data. /// Note: Byte[] is returned as Base64 string. internal List FetchData(string selectSql, SqlConnection connection, bool upperCaseKeys) { @@ -99,13 +104,13 @@ internal List FetchData(string selectSql, SqlConnection connecti } /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// /// Object entity to return data mapped into. /// 'Select' query. - /// 'Sql' Connection. - /// Enables fetch data only for object properties existing in database query result. - /// List of data rows mapped into object entity into a list for multiple rows of data. + /// The object used to connect to the database. + /// Enables fetch data only for object type properties existing in database query result. + /// List of data rows mapped into object of type . internal List FetchData(string selectSql, SqlConnection connection, bool strict) where T : new() { var dataList = new List(); @@ -165,11 +170,11 @@ internal T ExecuteScalar(string sqlStatement, SqlConnection connection) } /// - /// Executes SQL commands. + /// Executes a SQL statement that does not return a result set. /// - /// SQL statement as command. - /// 'Sql' Connection. - /// The number of rows affected. + /// SQL statement to execute. + /// The object used to connect to the database. + /// The number of rows affected by the execution of the SQL statement. internal int ExecuteCommand(string sqlStatement, SqlConnection connection) { using(var sqlCommand = GetSqlCommand(sqlStatement, connection, CommandType.Text)) @@ -182,15 +187,14 @@ internal int ExecuteCommand(string sqlStatement, SqlConnection connection) /// Executes multiple SQL statements within a transaction to ensure atomicity. /// /// A list of SQL statements to execute. - /// The SQL database connection. + /// The object used to connect to the database. /// - /// Returns true if the transaction is committed successfully; + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. /// - /// - /// Logs and handles exceptions if any SQL command execution fails. - /// - internal static bool ExecuteTransaction(List sqlStatements, SqlConnection connection) + internal static Result ExecuteTransaction(List sqlStatements, SqlConnection connection) { using (SqlTransaction transaction = GetSqlTransaction(connection)) { @@ -204,15 +208,211 @@ internal static bool ExecuteTransaction(List sqlStatements, SqlConnectio } } transaction.Commit(); - return true; + return new Result { Success = true }; + } + catch (Exception ex) + { + transaction.Rollback(); + return new Result { Success = false, Exception = ex }; + } + } + } + + #endregion + + #region Asynchronous + + /// + /// Asynchronously executes the specified SQL command and returns 'SQL Server' data reader. + /// + /// The text of the query. + /// The object used to connect to the database. + /// Sql command type. + /// A object that can be used to read the query results. + internal async Task GetSqlReaderAsync(string cmdText, SqlConnection connection, CommandType commandType) + { + await connection.OpenAsync(); + using (var sqlCommand = new SqlCommand(cmdText, connection) { CommandType = commandType }) + { + return await sqlCommand.ExecuteReaderAsync(); + } + } + + /// + /// Asynchronously creates and returns a new with the specified command text, + /// connection, and command type. Opens the connection before creating the command. + /// + /// The SQL command text to execute. + /// The object used to connect to the database. + /// The type of the command (e.g., Text, StoredProcedure). + /// A configured instance. + internal async Task GetSqlCommandAsync(string cmdText, SqlConnection connection, CommandType commandType) + { + await connection.OpenAsync(); + var sqlCommand = new SqlCommand(cmdText, connection) { CommandType = commandType }; + return sqlCommand; + } + + /// + /// Asynchronously initiates and returns a new SQL transaction for the specified database connection. + /// + /// The object used to connect to the database. + /// A new associated with the provided connection. + internal static async Task GetSqlTransactionAsync(SqlConnection connection) + { + await connection.OpenAsync(); + var sqlTransaction = connection.BeginTransaction(); + return sqlTransaction; + } + + /// + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// Converts column names to keys holding values, with multiple database rows returned into a list. + /// Note: Use aliases in query for similar column names. + /// + /// 'Select' query. + /// The object used to connect to the database. + /// Boolean parameter to return dictionary keys in uppercase. + /// List of with column names as keys holding values into a list for multiple rows of data. + /// Note: Byte[] is returned as Base64 string. + internal async Task> FetchDataAsync(string selectSql, SqlConnection connection, bool upperCaseKeys) + { + var dataList = new List(); + using (var reader = await GetSqlReaderAsync(selectSql, connection, CommandType.Text)) + { + while (await reader.ReadAsync()) + { + var addRow = new DataDictionary(); + for (int i = 0; i < reader.FieldCount; i++) + { + string key = upperCaseKeys ? reader.GetName(i).ToUpper() : reader.GetName(i); + if (reader.GetValue(i) is byte[] value) + addRow.ReferenceData.Add(key, Convert.ToBase64String(value)); + else + addRow.ReferenceData.Add(key, reader.GetValue(i).ToString()); + } + dataList.Add(addRow); + } + } + return dataList; + } + + /// + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// + /// Object entity to return data mapped into. + /// 'Select' query. + /// The object used to connect to the database. + /// Enables fetch data only for object type properties existing in database query result. + /// List of data rows mapped into object of type . + internal async Task> FetchDataAsync(string selectSql, SqlConnection connection, bool strict) where T : new() + { + var dataList = new List(); + using (var reader = await GetSqlReaderAsync(selectSql, connection, CommandType.Text)) + { + while (await reader.ReadAsync()) + { + var addObjectRow = new T(); + foreach (var prop in typeof(T).GetProperties()) + { + if ((strict || Utils.ColumnExists(reader, prop.Name)) && !await reader.IsDBNullAsync(reader.GetOrdinal(prop.Name))) + prop.SetValue(addObjectRow, reader[prop.Name]); + } + dataList.Add(addObjectRow); + } + } + return dataList; + } + + /// + /// Asynchronously executes the provided SQL statement using the given SQL connection and returns the first column of the first row in the result set. + /// If the result is DBNull, an empty string is returned. + /// + /// The SQL statement to execute. It should be a query that returns a single value. + /// The to use for executing the SQL statement. + /// + /// A representing the value of the first column of the first row in the result set, + /// or an empty string if the result is DBNull. + /// + internal async Task ExecuteScalarAsync(string sqlStatement, SqlConnection connection) + { + using (var sqlCommand = await GetSqlCommandAsync(sqlStatement, connection, CommandType.Text)) + { + var result = await sqlCommand.ExecuteScalarAsync(); + return result == DBNull.Value ? string.Empty : result.ToString(); + } + } + + /// + /// Asynchronously executes the provided SQL statement using the given SQL connection and returns the first column of the first row in the result set, + /// converted to the specified type . If the result is DBNull, the default value of is returned. + /// + /// The type to which the result should be converted. + /// The SQL statement to execute. It should be a query that returns a single value. + /// The to use for executing the SQL statement. + /// + /// The value of the first column of the first row in the result set, converted to type , + /// or the default value of if the result is DBNull. + /// + internal async Task ExecuteScalarAsync(string sqlStatement, SqlConnection connection) + { + using (var sqlCommand = await GetSqlCommandAsync(sqlStatement, connection, CommandType.Text)) + { + var result = await sqlCommand.ExecuteScalarAsync(); + return result == DBNull.Value ? default : (T)Convert.ChangeType(result, typeof(T)); + } + } + + /// + /// Asynchronously executes a SQL statement that does not return a result set. + /// + /// SQL statement to execute. + /// The object used to connect to the database. + /// The number of rows affected by the execution of the SQL statement. + internal async Task ExecuteCommandAsync(string sqlStatement, SqlConnection connection) + { + using (var sqlCommand = await GetSqlCommandAsync(sqlStatement, connection, CommandType.Text)) + { + return await sqlCommand.ExecuteNonQueryAsync(); + } + } + + /// + /// Asynchronously executes multiple SQL statements within a transaction to ensure atomicity. + /// + /// A list of SQL statements to execute. + /// The object used to connect to the database. + /// + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. + /// + internal static async Task ExecuteTransactionAsync(List sqlStatements, SqlConnection connection) + { + using (SqlTransaction transaction = await GetSqlTransactionAsync(connection)) + { + try + { + foreach (var sqlStatement in sqlStatements) + { + using (var sqlCommand = GetSqlCommand(sqlStatement, connection, transaction)) + { + await sqlCommand.ExecuteNonQueryAsync(); + } + } + transaction.Commit(); + return new Result { Success = true}; } catch (Exception ex) { transaction.Rollback(); - Console.WriteLine($"Transaction rolled back due to error: {ex.Message}"); - return false; + return new Result { Success = false, Exception = ex }; } } } + + #endregion + } } diff --git a/QueryDB/MySQL/Adapter.cs b/QueryDB/MySQL/Adapter.cs index c3081dc..d26e39c 100644 --- a/QueryDB/MySQL/Adapter.cs +++ b/QueryDB/MySQL/Adapter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Threading.Tasks; namespace QueryDB.MySQL { @@ -11,13 +12,16 @@ namespace QueryDB.MySQL /// internal class Adapter { + + #region Synchronous + /// - /// Gets the 'MySQL' data reader. + /// Executes the specified SQL command and returns 'MySQL' data reader. /// /// The text of the query. - /// 'MySQL' connection. + /// The object used to connect to the database. /// Sql command type. - /// 'MySQL' data reader. + /// A object that can be used to read the query results. internal MySqlDataReader GetMySqlReader(string cmdText, MySqlConnection connection, CommandType commandType) { connection.Open(); @@ -32,7 +36,7 @@ internal MySqlDataReader GetMySqlReader(string cmdText, MySqlConnection connecti /// connection, and command type. Opens the connection before creating the command. /// /// The SQL command text to execute. - /// The to use. + /// The object used to connect to the database. /// The type of the command (e.g., Text, StoredProcedure). /// A configured instance. internal MySqlCommand GetMySqlCommand(string cmdText, MySqlConnection connection, CommandType commandType) @@ -58,7 +62,7 @@ internal static MySqlCommand GetMySqlCommand(string cmdText, MySqlConnection con /// /// Initiates and returns a new MySQL transaction for the given database connection. /// - /// The MySQL database connection. + /// The object used to connect to the database. /// A new associated with the provided connection. internal static MySqlTransaction GetMySqlTransaction(MySqlConnection connection) { @@ -68,13 +72,14 @@ internal static MySqlTransaction GetMySqlTransaction(MySqlConnection connection) } /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// Converts column names to keys holding values, with multiple database rows returned into a list. + /// Note: Use aliases in query for similar column names. /// /// 'Select' query. - /// 'MySQL' Connection. + /// The object used to connect to the database. /// Boolean parameter to return dictionary keys in uppercase. - /// List of data Dictionary with column names as keys holding values into a list for multiple rows of data. + /// List of with column names as keys holding values into a list for multiple rows of data. /// Note: Byte[] is returned as Base64 string. internal List FetchData(string selectSql, MySqlConnection connection, bool upperCaseKeys) { @@ -99,13 +104,13 @@ internal List FetchData(string selectSql, MySqlConnection connec } /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// /// Object entity to return data mapped into. /// 'Select' query. - /// 'MySQL' Connection. - /// Enables fetch data only for object properties existing in database query result. - /// List of data rows mapped into object entity into a list for multiple rows of data. + /// The object used to connect to the database. + /// Enables fetch data only for object type properties existing in database query result. + /// List of data rows mapped into object of type . internal List FetchData(string selectSql, MySqlConnection connection, bool strict) where T : new() { var dataList = new List(); @@ -165,11 +170,11 @@ internal T ExecuteScalar(string sqlStatement, MySqlConnection connection) } /// - /// Executes SQL commands. + /// Executes a SQL statement that does not return a result set. /// - /// SQL statement as command. - /// 'MySQL' Connection. - /// The number of rows affected. + /// SQL statement to execute. + /// The object used to connect to the database. + /// The number of rows affected by the execution of the SQL statement. internal int ExecuteCommand(string sqlStatement, MySqlConnection connection) { using (var sqlCommand = GetMySqlCommand(sqlStatement, connection, CommandType.Text)) @@ -182,15 +187,14 @@ internal int ExecuteCommand(string sqlStatement, MySqlConnection connection) /// Executes multiple SQL statements within a MySQL transaction to ensure atomicity. /// /// A list of SQL statements to execute. - /// The MySQL database connection. + /// The object used to connect to the database. /// - /// Returns true if the transaction is committed successfully; + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. /// - /// - /// Logs and handles exceptions if any SQL command execution fails. - /// - internal static bool ExecuteTransaction(List sqlStatements, MySqlConnection connection) + internal static Result ExecuteTransaction(List sqlStatements, MySqlConnection connection) { using (MySqlTransaction transaction = GetMySqlTransaction(connection)) { @@ -204,15 +208,209 @@ internal static bool ExecuteTransaction(List sqlStatements, MySqlConnect } } transaction.Commit(); - return true; + return new Result { Success = true }; + } + catch (Exception ex) + { + transaction.Rollback(); + return new Result { Success = false, Exception = ex }; + } + } + } + + #endregion + + #region Asynchronous + + /// + /// Asynchronously executes the specified SQL command and returns 'MySQL' data reader. + /// + /// The text of the query. + /// The object used to connect to the database. + /// Sql command type. + /// A object that can be used to read the query results. + internal async Task GetMySqlReaderAsync(string cmdText, MySqlConnection connection, CommandType commandType) + { + await connection.OpenAsync(); + using (var sqlCommand = new MySqlCommand(cmdText, connection) { CommandType = commandType }) + { + return (MySqlDataReader) await sqlCommand.ExecuteReaderAsync(); + } + } + + /// + /// Asynchronously creates and returns a new with the specified command text, + /// connection, and command type. Opens the connection before creating the command. + /// + /// The SQL command text to execute. + /// The object used to connect to the database. + /// The type of the command (e.g., Text, StoredProcedure). + /// A configured instance. + internal async Task GetMySqlCommandAsync(string cmdText, MySqlConnection connection, CommandType commandType) + { + await connection.OpenAsync(); + var sqlCommand = new MySqlCommand(cmdText, connection) { CommandType = commandType }; + return sqlCommand; + } + + /// + /// Asynchronously initiates and returns a new MySQL transaction for the given database connection. + /// + /// The object used to connect to the database. + /// A new associated with the provided connection. + internal static async Task GetMySqlTransactionAsync(MySqlConnection connection) + { + await connection.OpenAsync(); + var mySqlTransaction = await connection.BeginTransactionAsync(); + return mySqlTransaction; + } + + /// + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// Converts column names to keys holding values, with multiple database rows returned into a list. + /// Note: Use aliases in query for similar column names. + /// + /// 'Select' query. + /// The object used to connect to the database. + /// Boolean parameter to return dictionary keys in uppercase. + /// List of with column names as keys holding values into a list for multiple rows of data. + internal async Task> FetchDataAsync(string selectSql, MySqlConnection connection, bool upperCaseKeys) + { + var dataList = new List(); + using (var reader = await GetMySqlReaderAsync(selectSql, connection, CommandType.Text)) + { + while (await reader.ReadAsync()) + { + var addRow = new DataDictionary(); + for (int i = 0; i < reader.FieldCount; i++) + { + string key = upperCaseKeys ? reader.GetName(i).ToUpper() : reader.GetName(i); + if (reader.GetValue(i) is byte[] value) + addRow.ReferenceData.Add(key, Convert.ToBase64String(value)); + else + addRow.ReferenceData.Add(key, reader.GetValue(i).ToString()); + } + dataList.Add(addRow); + } + } + return dataList; + } + + /// + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// + /// Object entity to return data mapped into. + /// 'Select' query. + /// The object used to connect to the database. + /// Enables fetch data only for object type properties existing in database query result. + /// List of data rows mapped into object of type . + internal async Task> FetchDataAsync(string selectSql, MySqlConnection connection, bool strict) where T : new() + { + var dataList = new List(); + using (var reader = await GetMySqlReaderAsync(selectSql, connection, CommandType.Text)) + { + while (await reader.ReadAsync()) + { + var addObjectRow = new T(); + foreach (var prop in typeof(T).GetProperties()) + { + if ((strict || Utils.ColumnExists(reader, prop.Name)) && !await reader.IsDBNullAsync(reader.GetOrdinal(prop.Name))) + prop.SetValue(addObjectRow, reader[prop.Name]); + } + dataList.Add(addObjectRow); + } + } + return dataList; + } + + /// + /// Asynchronously executes the provided SQL statement using the given MySQL connection and returns the first column of the first row in the result set. + /// If the result is DBNull, an empty string is returned. + /// + /// The SQL statement to execute. It should be a query that returns a single value. + /// The to use for executing the SQL statement. + /// + /// A representing the value of the first column of the first row in the result set, + /// or an empty string if the res + internal async Task ExecuteScalarAsync(string sqlStatement, MySqlConnection connection) + { + using (var sqlCommand = await GetMySqlCommandAsync(sqlStatement, connection, CommandType.Text)) + { + var result = await sqlCommand.ExecuteScalarAsync(); + return result == DBNull.Value ? string.Empty : result.ToString(); + } + } + + /// + /// Asynchronously executes the provided SQL statement using the given MySQL connection and returns the first column of the first row in the result set, + /// converted to the specified type . If the result is DBNull, the default value of is returned. + /// + /// The type to which the result should be converted. + /// The SQL statement to execute. It should be a query that returns a single value. + /// The to use for executing the SQL statement. + /// + /// The value of the first column of the first row in the result set, converted to type , + /// or the default value of if the result is DBNull. + /// + internal async Task ExecuteScalarAsync(string sqlStatement, MySqlConnection connection) + { + using (var sqlCommand = await GetMySqlCommandAsync(sqlStatement, connection, CommandType.Text)) + { + var result = await sqlCommand.ExecuteScalarAsync(); + return result == DBNull.Value ? default : (T)Convert.ChangeType(result, typeof(T)); + } + } + + /// + /// Asynchronously executes a SQL statement that does not return a result set. + /// + /// SQL statement to execute. + /// The object used to connect to the database. + /// The number of rows affected by the execution of the SQL statement. + internal async Task ExecuteCommandAsync(string sqlStatement, MySqlConnection connection) + { + using (var sqlCommand = await GetMySqlCommandAsync(sqlStatement, connection, CommandType.Text)) + { + return await sqlCommand.ExecuteNonQueryAsync(); + } + } + + /// + /// Asynchronously executes multiple SQL statements within a MySQL transaction to ensure atomicity. + /// + /// A list of SQL statements to execute. + /// The object used to connect to the database. + /// + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. + /// + internal static async Task ExecuteTransactionAsync(List sqlStatements, MySqlConnection connection) + { + using (MySqlTransaction transaction = await GetMySqlTransactionAsync(connection)) + { + try + { + foreach (var sqlStatement in sqlStatements) + { + using (var sqlCommand = GetMySqlCommand(sqlStatement, connection, transaction)) + { + await sqlCommand.ExecuteNonQueryAsync(); + } + } + transaction.Commit(); + return new Result { Success = true }; } catch (Exception ex) { transaction.Rollback(); - Console.WriteLine($"Transaction rolled back due to error: {ex.Message}"); - return false; + return new Result { Success = false, Exception = ex }; } } } + + #endregion + } } \ No newline at end of file diff --git a/QueryDB/Oracle/Adapter.cs b/QueryDB/Oracle/Adapter.cs index 526d25c..82e9764 100644 --- a/QueryDB/Oracle/Adapter.cs +++ b/QueryDB/Oracle/Adapter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Threading.Tasks; namespace QueryDB.Oracle { @@ -11,13 +12,16 @@ namespace QueryDB.Oracle /// internal class Adapter { + + #region Synchronous + /// - /// Gets the 'Oracle' data reader. + /// Executes the specified SQL command and returns 'Oracle' data reader. /// /// The text of the query. - /// 'Oracle' connection. + /// The object used to connect to the database. /// Sql command type. - /// 'Oracle' data reader. + /// A object that can be used to read the query results. internal OracleDataReader GetOracleReader(string cmdText, OracleConnection connection, CommandType commandType) { connection.Open(); @@ -32,7 +36,7 @@ internal OracleDataReader GetOracleReader(string cmdText, OracleConnection conne /// connection, and command type. Opens the connection before creating the command. /// /// The SQL command text to execute. - /// The to use. + /// The object used to connect to the database. /// The type of the command (e.g., Text, StoredProcedure). /// A configured instance. internal OracleCommand GetOracleCommand(string cmdText, OracleConnection connection, CommandType commandType) @@ -57,7 +61,7 @@ internal static OracleCommand GetOracleCommand(string cmdText, OracleConnection /// /// Initiates and returns a new Oracle transaction for the specified database connection. /// - /// The Oracle database connection. + /// The object used to connect to the database. /// A new associated with the provided connection. internal static OracleTransaction GetOracleTransaction(OracleConnection connection) { @@ -67,13 +71,14 @@ internal static OracleTransaction GetOracleTransaction(OracleConnection connecti } /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// Converts column names to keys holding values, with multiple database rows returned into a list. + /// Note: Use aliases in query for similar column names. /// /// 'Select' query. - /// 'Oracle' Connection. + /// The object used to connect to the database. /// Boolean parameter to return dictionary keys in uppercase. - /// List of data Dictionary with column names as keys holding values into a list for multiple rows of data. + /// List of with column names as keys holding values into a list for multiple rows of data. /// Note: Byte[]/BFile is returned as Base64 string. internal List FetchData(string selectSql, OracleConnection connection, bool upperCaseKeys) { @@ -100,13 +105,13 @@ internal List FetchData(string selectSql, OracleConnection conne } /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// /// Object entity to return data mapped into. /// 'Select' query. - /// 'Oracle' Connection. - /// Enables fetch data only for object properties existing in database query result. - /// List of data rows mapped into object entity into a list for multiple rows of data. + /// The object used to connect to the database. + /// Enables fetch data only for object type properties existing in database query result. + /// List of data rows mapped into object of type . internal List FetchData(string selectSql, OracleConnection connection, bool strict) where T : new() { var dataList = new List(); @@ -123,7 +128,7 @@ internal List FetchData(string selectSql, OracleConnection conne prop.SetValue(addObjectRow, Utils.GetBFileByteContent(reader, prop.Name)); else prop.SetValue(addObjectRow, reader[prop.Name]); - } + } } dataList.Add(addObjectRow); } @@ -171,11 +176,11 @@ internal T ExecuteScalar(string sqlStatement, OracleConnection connection) } /// - /// Executes SQL commands. + /// Executes a SQL statement that does not return a result set. /// - /// SQL statement as command. - /// 'Oracle' Connection. - /// The number of rows affected. + /// SQL statement to execute. + /// The object used to connect to the database. + /// The number of rows affected by the execution of the SQL statement. internal int ExecuteCommand(string sqlStatement, OracleConnection connection) { using (var sqlCommand = GetOracleCommand(sqlStatement, connection, CommandType.Text)) @@ -188,15 +193,14 @@ internal int ExecuteCommand(string sqlStatement, OracleConnection connection) /// Executes multiple SQL statements within an Oracle transaction to ensure atomicity. /// /// A list of SQL statements to execute. - /// The Oracle database connection. + /// The object used to connect to the database. /// - /// Returns true if the transaction is committed successfully; + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. /// - /// - /// Logs and handles exceptions if any SQL command execution fails. - /// - internal static bool ExecuteTransaction(List sqlStatements, OracleConnection connection) + internal static Result ExecuteTransaction(List sqlStatements, OracleConnection connection) { using (OracleTransaction transaction = GetOracleTransaction(connection)) { @@ -211,15 +215,219 @@ internal static bool ExecuteTransaction(List sqlStatements, OracleConnec } } transaction.Commit(); - return true; + return new Result { Success = true }; } catch (Exception ex) { transaction.Rollback(); - Console.WriteLine($"Transaction rolled back due to error: {ex.Message}"); - return false; + return new Result { Success = false, Exception = ex }; + } + } + } + + #endregion + + #region Asynchronous + + /// + /// Asynchronously executes the specified SQL command and returns 'Oracle' data reader. + /// + /// The text of the query. + /// The object used to connect to the database. + /// Sql command type. + /// A object that can be used to read the query results. + internal async Task GetOracleReaderAsync(string cmdText, OracleConnection connection, CommandType commandType) + { + await connection.OpenAsync(); + using (var sqlCommand = new OracleCommand(cmdText, connection) { CommandType = commandType }) + { + return (OracleDataReader) await sqlCommand.ExecuteReaderAsync(); + } + } + + /// + /// Asynchronously creates and returns a new with the specified command text, + /// connection, and command type. Opens the connection before creating the command. + /// + /// The SQL command text to execute. + /// The object used to connect to the database. + /// The type of the command (e.g., Text, StoredProcedure). + /// A configured instance. + internal async Task GetOracleCommandAsync(string cmdText, OracleConnection connection, CommandType commandType) + { + await connection.OpenAsync(); + var sqlCommand = new OracleCommand(cmdText, connection) { CommandType = commandType }; + return sqlCommand; + } + + /// + /// Asynchronously initiates and returns a new Oracle transaction for the specified database connection. + /// + /// The object used to connect to the database. + /// A new associated with the provided connection. + internal static async Task GetOracleTransactionAsync(OracleConnection connection) + { + await connection.OpenAsync(); + var oracleTransaction = connection.BeginTransaction(); + return oracleTransaction; + } + + /// + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// Converts column names to keys holding values, with multiple database rows returned into a list. + /// Note: Use aliases in query for similar column names. + /// + /// 'Select' query. + /// The object used to connect to the database. + /// Boolean parameter to return dictionary keys in uppercase. + /// List of with column names as keys holding values into a list for multiple rows of data. + /// Note: Byte[]/BFile is returned as Base64 string. + internal async Task> FetchDataAsync(string selectSql, OracleConnection connection, bool upperCaseKeys) + { + var dataList = new List(); + using (var reader = await GetOracleReaderAsync(selectSql, connection, CommandType.Text)) + { + while (await reader.ReadAsync()) + { + var addRow = new DataDictionary(); + for (int i = 0; i < reader.FieldCount; i++) + { + string key = upperCaseKeys ? reader.GetName(i).ToUpper() : reader.GetName(i); + if (Utils.IsBFileColumn(reader, i)) + addRow.ReferenceData.Add(key, await Utils.GetBFileBase64ContentAsync(reader, i)); + else if (reader.GetValue(i) is byte[] value) + addRow.ReferenceData.Add(key, Convert.ToBase64String(value)); + else + addRow.ReferenceData.Add(key, reader.GetValue(i).ToString()); + } + dataList.Add(addRow); } } + return dataList; } + + /// + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// + /// Object entity to return data mapped into. + /// 'Select' query. + /// The object used to connect to the database. + /// Enables fetch data only for object type properties existing in database query result. + /// List of data rows mapped into object of type . + internal async Task> FetchDataAsync(string selectSql, OracleConnection connection, bool strict) where T : new() + { + var dataList = new List(); + using (var reader = await GetOracleReaderAsync(selectSql, connection, CommandType.Text)) + { + while (await reader.ReadAsync()) + { + var addObjectRow = new T(); + foreach (var prop in typeof(T).GetProperties()) + { + if ((strict || Utils.ColumnExists(reader, prop.Name)) && !await reader.IsDBNullAsync(reader.GetOrdinal(prop.Name))) + { + if (Utils.IsBFileColumn(reader, prop.Name)) + prop.SetValue(addObjectRow, await Utils.GetBFileByteContentAsync(reader, prop.Name)); + else + prop.SetValue(addObjectRow, reader[prop.Name]); + } + } + dataList.Add(addObjectRow); + } + } + return dataList; + } + + /// + /// Asynchronously executes the provided SQL statement using the given Oracle connection and returns the first column of the first row in the result set. + /// If the result is DBNull, an empty string is returned. + /// + /// The SQL statement to execute. It should be a query that returns a single value. + /// The to use for executing the SQL statement. + /// + /// A representing the value of the first column of the first row in the result set, + /// or an empty string if the result is DBNull. + /// + internal async Task ExecuteScalarAsync(string sqlStatement, OracleConnection connection) + { + using (var sqlCommand = await GetOracleCommandAsync(sqlStatement, connection, CommandType.Text)) + { + var result = await sqlCommand.ExecuteScalarAsync(); + return result == DBNull.Value ? string.Empty : result.ToString(); + } + } + + /// + /// Asynchronously executes the provided SQL statement using the given Oracle connection and returns the first column of the first row in the result set, + /// converted to the specified type . If the result is DBNull, the default value of is returned. + /// + /// The type to which the result should be converted. + /// The SQL statement to execute. It should be a query that returns a single value. + /// The to use for executing the SQL statement. + /// + /// The value of the first column of the first row in the result set, converted to type , + /// or the default value of if the result is DBNull. + /// + internal async Task ExecuteScalarAsync(string sqlStatement, OracleConnection connection) + { + using (var sqlCommand = await GetOracleCommandAsync(sqlStatement, connection, CommandType.Text)) + { + var result = await sqlCommand.ExecuteScalarAsync(); + return result == null || result == DBNull.Value ? default : (T)Convert.ChangeType(result, typeof(T)); + } + } + + /// + /// Asynchronously executes a SQL statement that does not return a result set. + /// + /// SQL statement to execute. + /// The object used to connect to the database. + /// The number of rows affected by the execution of the SQL statement. + internal async Task ExecuteCommandAsync(string sqlStatement, OracleConnection connection) + { + using (var sqlCommand = await GetOracleCommandAsync(sqlStatement, connection, CommandType.Text)) + { + return await sqlCommand.ExecuteNonQueryAsync(); + } + } + + /// + /// Asynchronously executes multiple SQL statements within an Oracle transaction to ensure atomicity. + /// + /// A list of SQL statements to execute. + /// The object used to connect to the database. + /// + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. + /// + internal static async Task ExecuteTransactionAsync(List sqlStatements, OracleConnection connection) + { + using (OracleTransaction transaction = await GetOracleTransactionAsync(connection)) + { + try + { + foreach (var sqlStatement in sqlStatements) + { + using (var sqlCommand = GetOracleCommand(sqlStatement, connection)) + { + sqlCommand.Transaction = transaction; + await sqlCommand.ExecuteNonQueryAsync(); + } + } + transaction.Commit(); + return new Result { Success = true }; + } + catch (Exception ex) + { + transaction.Rollback(); + return new Result { Success = false, Exception = ex }; + } + } + } + + #endregion + } } \ No newline at end of file diff --git a/QueryDB/PostgreSQL/Adapter.cs b/QueryDB/PostgreSQL/Adapter.cs index 1239630..7c13feb 100644 --- a/QueryDB/PostgreSQL/Adapter.cs +++ b/QueryDB/PostgreSQL/Adapter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Threading.Tasks; namespace QueryDB.PostgreSQL { @@ -11,13 +12,16 @@ namespace QueryDB.PostgreSQL /// internal class Adapter { + + #region Synchronous + /// - /// Gets the 'PostgreSQL' data reader. + /// Executes the specified SQL command and returns 'PostgreSQL' data reader. /// /// The text of the query. - /// 'PostgreSQL' connection. + /// The object used to connect to the database. /// Sql command type. - /// 'PostgreSQL' data reader. + /// A object that can be used to read the query results. internal NpgsqlDataReader GetPostgreSqlReader(string cmdText, NpgsqlConnection connection, CommandType commandType) { connection.Open(); @@ -32,7 +36,7 @@ internal NpgsqlDataReader GetPostgreSqlReader(string cmdText, NpgsqlConnection c /// connection, and command type. Opens the connection before creating the command. /// /// The SQL command text to execute. - /// The to use. + /// The object used to connect to the database. /// The type of the command (e.g., Text, StoredProcedure). /// A configured instance. internal NpgsqlCommand GetPostgreSqlCommand(string cmdText, NpgsqlConnection connection, CommandType commandType) @@ -58,7 +62,7 @@ internal static NpgsqlCommand GetPostgreSqlCommand(string cmdText, NpgsqlConnect /// /// Initiates and returns a new PostgreSQL transaction for the specified database connection. /// - /// The PostgreSQL database connection. + /// The object used to connect to the database. /// A new associated with the provided connection. internal static NpgsqlTransaction GetPostgreSqlTransaction(NpgsqlConnection connection) { @@ -68,13 +72,14 @@ internal static NpgsqlTransaction GetPostgreSqlTransaction(NpgsqlConnection conn } /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// Converts column names to keys holding values, with multiple database rows returned into a list. + /// Note: Use aliases in query for similar column names. /// /// 'Select' query. - /// 'PostgreSQL' Connection. + /// The object used to connect to the database. /// Boolean parameter to return dictionary keys in uppercase. - /// List of data Dictionary with column names as keys holding values into a list for multiple rows of data. + /// List of with column names as keys holding values into a list for multiple rows of data. /// Note: Byte[] is returned as Base64 string. internal List FetchData(string selectSql, NpgsqlConnection connection, bool upperCaseKeys) { @@ -99,13 +104,13 @@ internal List FetchData(string selectSql, NpgsqlConnection conne } /// - /// Retrieves records for 'Select' queries from the database. + /// Executes and retrieves records for 'Select' queries from the database. /// /// Object entity to return data mapped into. /// 'Select' query. - /// 'PostgreSQL' Connection. - /// Enables fetch data only for object properties existing in database query result. - /// List of data rows mapped into object entity into a list for multiple rows of data. + /// The object used to connect to the database. + /// Enables fetch data only for object type properties existing in database query result. + /// List of data rows mapped into object of type . internal List FetchData(string selectSql, NpgsqlConnection connection, bool strict) where T : new() { var dataList = new List(); @@ -165,11 +170,11 @@ internal T ExecuteScalar(string sqlStatement, NpgsqlConnection connection) } /// - /// Executes SQL commands. + /// Executes a SQL statement that does not return a result set. /// - /// SQL statement as command. - /// 'PostgreSQL' Connection. - /// The number of rows affected + /// SQL statement to execute. + /// The object used to connect to the database. + /// The number of rows affected by the execution of the SQL statement. internal int ExecuteCommand(string sqlStatement, NpgsqlConnection connection) { using (var sqlCommand = GetPostgreSqlCommand(sqlStatement, connection, CommandType.Text)) @@ -182,15 +187,14 @@ internal int ExecuteCommand(string sqlStatement, NpgsqlConnection connection) /// Executes multiple SQL statements within a PostgreSQL transaction to ensure atomicity. /// /// A list of SQL statements to execute. - /// The PostgreSQL database connection. + /// The object used to connect to the database. /// - /// Returns true if the transaction is committed successfully; + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. /// - /// - /// Logs and handles exceptions if any SQL command execution fails. - /// - internal static bool ExecuteTransaction(List sqlStatements, NpgsqlConnection connection) + internal static Result ExecuteTransaction(List sqlStatements, NpgsqlConnection connection) { using (NpgsqlTransaction transaction = GetPostgreSqlTransaction(connection)) { @@ -204,15 +208,211 @@ internal static bool ExecuteTransaction(List sqlStatements, NpgsqlConnec } } transaction.Commit(); - return true; + return new Result { Success = true }; } catch (Exception ex) { transaction.Rollback(); - Console.WriteLine($"Transaction rolled back due to error: {ex.Message}"); - return false; + return new Result { Success = false, Exception = ex }; + } + } + } + + #endregion + + #region Asynchronous + + /// + /// Asynchronously executes the specified SQL command and returns 'PostgreSQL' data reader. + /// + /// The text of the query. + /// The object used to connect to the database. + /// Sql command type. + /// A object that can be used to read the query results. + internal async Task GetPostgreSqlReaderAsync(string cmdText, NpgsqlConnection connection, CommandType commandType) + { + await connection.OpenAsync(); + using (var sqlCommand = new NpgsqlCommand(cmdText, connection) { CommandType = commandType }) + { + return await sqlCommand.ExecuteReaderAsync(); + } + } + + /// + /// Asynchronously creates and returns a new with the specified command text, + /// connection, and command type. Opens the connection before creating the command. + /// + /// The SQL command text to execute. + /// The object used to connect to the database. + /// The type of the command (e.g., Text, StoredProcedure). + /// A configured instance. + internal async Task GetPostgreSqlCommandAsync(string cmdText, NpgsqlConnection connection, CommandType commandType) + { + await connection.OpenAsync(); + var sqlCommand = new NpgsqlCommand(cmdText, connection) { CommandType = commandType }; + return sqlCommand; + } + + /// + /// Asynchronously initiates and returns a new PostgreSQL transaction for the specified database connection. + /// + /// The object used to connect to the database. + /// A new associated with the provided connection. + internal static async Task GetPostgreSqlTransactionAsync(NpgsqlConnection connection) + { + await connection.OpenAsync(); + var npgsqlTransaction = connection.BeginTransaction(); + return npgsqlTransaction; + + } + /// + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// Converts column names to keys holding values, with multiple database rows returned into a list. + /// Note: Use aliases in query for similar column names. + /// + /// 'Select' query. + /// The object used to connect to the database. + /// Boolean parameter to return dictionary keys in uppercase. + /// List of with column names as keys holding values into a list for multiple rows of data. + /// Note: Byte[] is returned as Base64 string. + internal async Task> FetchDataAsync(string selectSql, NpgsqlConnection connection, bool upperCaseKeys) + { + var dataList = new List(); + using (var reader = await GetPostgreSqlReaderAsync(selectSql, connection, CommandType.Text)) + { + while (await reader.ReadAsync()) + { + var addRow = new DataDictionary(); + for (int i = 0; i < reader.FieldCount; i++) + { + string key = upperCaseKeys ? reader.GetName(i).ToUpper() : reader.GetName(i); + if (reader.GetValue(i) is byte[] value) + addRow.ReferenceData.Add(key, Convert.ToBase64String(value)); + else + addRow.ReferenceData.Add(key, reader.GetValue(i).ToString()); + } + dataList.Add(addRow); + } + } + return dataList; + } + + /// + /// Asynchronously executes and retrieves records for 'Select' queries from the database. + /// + /// Object entity to return data mapped into. + /// 'Select' query. + /// The object used to connect to the database. + /// Enables fetch data only for object type properties existing in database query result. + /// List of data rows mapped into object of type . + internal async Task> FetchDataAsync(string selectSql, NpgsqlConnection connection, bool strict) where T : new() + { + var dataList = new List(); + using (var reader = await GetPostgreSqlReaderAsync(selectSql, connection, CommandType.Text)) + { + while (await reader.ReadAsync()) + { + var addObjectRow = new T(); + foreach (var prop in typeof(T).GetProperties()) + { + if ((strict || Utils.ColumnExists(reader, prop.Name)) && !await reader.IsDBNullAsync(reader.GetOrdinal(prop.Name))) + prop.SetValue(addObjectRow, reader[prop.Name]); + } + dataList.Add(addObjectRow); } } + return dataList; + } + + /// + /// Asynchronously executes the provided SQL statement using the given PostgreSQL connection and returns the first column of the first row in the result set. + /// If the result is DBNull, an empty string is returned. + /// + /// The SQL statement to execute. It should be a query that returns a single value. + /// The to use for executing the SQL statement. + /// + /// A representing the value of the first column of the first row in the result set, + /// or an empty string if the result is DBNull. + /// + internal async Task ExecuteScalarAsync(string sqlStatement, NpgsqlConnection connection) + { + using (var sqlCommand = await GetPostgreSqlCommandAsync(sqlStatement, connection, CommandType.Text)) + { + var result = await sqlCommand.ExecuteScalarAsync(); + return result == null || result == DBNull.Value ? string.Empty : result.ToString(); + } } + + /// + /// Asynchronously executes the provided SQL statement using the given PostgreSQL connection and returns the first column of the first row in the result set, + /// converted to the specified type . If the result is DBNull, the default value of is returned. + /// + /// The type to which the result should be converted. + /// The SQL statement to execute. It should be a query that returns a single value. + /// The to use for executing the SQL statement. + /// + /// The value of the first column of the first row in the result set, converted to type , + /// or the default value of if the result is DBNull. + /// + internal async Task ExecuteScalarAsync(string sqlStatement, NpgsqlConnection connection) + { + using (var sqlCommand = await GetPostgreSqlCommandAsync(sqlStatement, connection, CommandType.Text)) + { + var result = await sqlCommand.ExecuteScalarAsync(); + return result == null || result == DBNull.Value ? default : (T)Convert.ChangeType(result, typeof(T)); + } + } + + /// + /// Asynchronously executes a SQL statement that does not return a result set. + /// + /// SQL statement to execute. + /// The object used to connect to the database. + /// The number of rows affected by the execution of the SQL statement. + internal async Task ExecuteCommandAsync(string sqlStatement, NpgsqlConnection connection) + { + using (var sqlCommand = await GetPostgreSqlCommandAsync(sqlStatement, connection, CommandType.Text)) + { + return await sqlCommand.ExecuteNonQueryAsync(); + } + } + + /// + /// Asynchronously executes multiple SQL statements within a PostgreSQL transaction to ensure atomicity. + /// + /// A list of SQL statements to execute. + /// The object used to connect to the database. + /// + /// A object indicating the outcome of the transaction. + /// The property is true if the transaction is committed successfully; + /// otherwise, false if an error occurs and the transaction is rolled back. + /// If an error occurs, the property contains the exception details. + /// + internal static async Task ExecuteTransactionAsync(List sqlStatements, NpgsqlConnection connection) + { + using (NpgsqlTransaction transaction = await GetPostgreSqlTransactionAsync(connection)) + { + try + { + foreach (var sqlStatement in sqlStatements) + { + using (var sqlCommand = GetPostgreSqlCommand(sqlStatement, connection, transaction)) + { + await sqlCommand.ExecuteNonQueryAsync(); + } + } + await transaction.CommitAsync(); + return new Result { Success = true }; + } + catch (Exception ex) + { + await transaction.RollbackAsync(); + return new Result { Success = false, Exception = ex }; + } + } + } + + #endregion + } } diff --git a/QueryDB/QueryDB.csproj b/QueryDB/QueryDB.csproj index 90e834f..e1f90ba 100644 --- a/QueryDB/QueryDB.csproj +++ b/QueryDB/QueryDB.csproj @@ -15,14 +15,16 @@ Features: - Retrieve data from the database. - Execute scalar queries (returning a single value). - Execute non-query database commands. -- Execute transactions while maintaining atomicity. +- Execute transactions while maintaining atomicity. +- Support for Synchronous and Asynchronous operations. https://github.com/abhinavminhas/QueryDB.NET https://github.com/abhinavminhas/QueryDB.NET git LICENSE sql, query, rdbms, database, mssql, sqlserver, mysql, oracle, postgresql - 1. Execute scalar queries (returning a single value). - 1.1.0 + 1. Asynchronous operations. +2. Execute transaction to return transaction outcome and exception details in case of failures. + 1.2.0 diff --git a/QueryDB/Resources/Result.cs b/QueryDB/Resources/Result.cs new file mode 100644 index 0000000..dc66d2d --- /dev/null +++ b/QueryDB/Resources/Result.cs @@ -0,0 +1,20 @@ +using System; + +namespace QueryDB.Resources +{ + /// + /// Represents the outcome of an operation, indicating success or failure. + /// + public class Result + { + /// + /// Gets or sets a value indicating whether the operation was successful. + /// + public bool Success { get; internal set; } + + /// + /// Gets or sets the exception that occurred during the operation, if any. + /// + public Exception Exception { get; internal set; } + } +} diff --git a/QueryDB/Resources/Utils.cs b/QueryDB/Resources/Utils.cs index 8a4bdf1..ca114c8 100644 --- a/QueryDB/Resources/Utils.cs +++ b/QueryDB/Resources/Utils.cs @@ -1,6 +1,7 @@ using Oracle.ManagedDataAccess.Client; using System; using System.Data; +using System.Threading.Tasks; namespace QueryDB.Resources { @@ -88,6 +89,34 @@ internal static string GetBFileBase64Content(OracleDataReader reader, int column return content; } + /// + /// Asynchronously retrieves the content of a BFILE column from an Oracle data reader as a Base64-encoded string. + /// + /// The Oracle data reader containing the BFILE column. + /// The index of the BFILE column to read. + /// Returns the BFILE content as a Base64-encoded string, or an empty string if the BFILE is null. + internal static async Task GetBFileBase64ContentAsync(OracleDataReader reader, int columnIndex) + { + string content = string.Empty; + var bFile = reader.GetOracleBFile(columnIndex); + if (bFile != null && !await reader.IsDBNullAsync(columnIndex)) + { + bFile.OpenFile(); + byte[] buffer = new byte[bFile.Length]; + int bytesReadTotal = 0; + while (bytesReadTotal < buffer.Length) + { + int bytesRead = await bFile.ReadAsync(buffer, bytesReadTotal, buffer.Length - bytesReadTotal); + if (bytesRead == 0) + break; + bytesReadTotal += bytesRead; + } + content = Convert.ToBase64String(buffer); + bFile.Close(); + } + return content; + } + /// /// Retrieves the content of a BFILE column from an Oracle data reader as a byte array, using the column name. /// @@ -115,5 +144,33 @@ internal static byte[] GetBFileByteContent(OracleDataReader reader, string colum } return buffer; } + + /// + /// Asynchronously retrieves the content of a BFILE column from an Oracle data reader as a byte array, using the column name. + /// + /// The Oracle data reader containing the BFILE column. + /// The name of the BFILE column to read. + /// Returns the BFILE content as a byte array, or null if the BFILE is null or the column value is null. + internal static async Task GetBFileByteContentAsync(OracleDataReader reader, string columnName) + { + byte[] buffer = null; + int columnIndex = reader.GetOrdinal(columnName); + var bFile = reader.GetOracleBFile(columnIndex); + if (bFile != null && !await reader.IsDBNullAsync(columnIndex)) + { + bFile.OpenFile(); + buffer = new byte[bFile.Length]; + int bytesReadTotal = 0; + while (bytesReadTotal < buffer.Length) + { + int bytesRead = await bFile.ReadAsync(buffer, bytesReadTotal, buffer.Length - bytesReadTotal); + if (bytesRead == 0) + break; + bytesReadTotal += bytesRead; + } + bFile.Close(); + } + return buffer; + } } } diff --git a/README.md b/README.md index f5b43a5..81892d3 100644 --- a/README.md +++ b/README.md @@ -32,21 +32,106 @@ Built on **.NET Standard 2.0** - ( [_Supported Versions_](https://learn.microsof - Execute scalar queries (returning a single value). - Execute non-query database commands (e.g. `INSERT`, `UPDATE`, `DELETE`). - Execute transactions while maintaining atomicity. +- Support for Synchronous and Asynchronous operations. ## Getting Started -- Setup DBContext with the database of your choice + +- _**Setup `DBContext` with the database of your choice :**_ - ``` + ``` csharp var dbContext = new DBContext(DB., ); ``` -- Execute DBContext commands +- _**Execute `DBContext` commands :**_ - ``` +
+ + Execute DBContext Commands Synchronously
+ + ``` csharp var result = dbContext.FetchData(); + ``` + ``` csharp var result = dbContext.FetchData(); + ``` + ``` csharp var result = dbContext.ExecuteScalar(); + ``` + ``` csharp var result = dbContext.ExecuteScalar(); + ``` + ``` csharp var result = dbContext.ExecuteCommand(); + ``` + ``` csharp var result = dbContext.ExecuteTransaction(); ``` + +
+ +
+ + Execute DBContext Commands Asynchronously
+ + ``` csharp + var result = dbContext.FetchDataAsync(); + ``` + ``` csharp + var result = dbContext.FetchDataAsync(); + ``` + ``` csharp + var result = dbContext.ExecuteScalarAsync(); + ``` + ``` csharp + var result = dbContext.ExecuteScalarAsync(); + ``` + ``` csharp + var result = dbContext.ExecuteCommandAsync(); + ``` + ``` csharp + var result = dbContext.ExecuteTransactionAsync(); + ``` + +
+ +## Examples + +> Data Retrieval +``` csharp +public class Orders +{ + public string Agent_Code { get; set; } + public string Agent { get; set; } + public string Agent_Name { get; set; } + public string Agent_Location { get; set; } + public string Cust_Code { get; set; } + public string Customer { get; set; } + public string Cust_Name { get; set; } + public string Customer_Location { get; set; } + public decimal Ord_Num { get; set; } + public decimal Ord_Amount { get; set; } + public decimal Advance_Amount { get; set; } + public string Ord_Description { get; set; } +} + +var sql = @"SELECT A.Agent_Code, A.Agent_Name, C.Cust_Code, C.Cust_Name, O.Ord_Num, O.Ord_Amount, O.Advance_Amount, O.Ord_Date, O.Ord_Description FROM Agents A INNER JOIN + Customer C ON C.Agent_Code = A.Agent_Code INNER JOIN + Orders O ON O.Cust_Code = C.Cust_Code AND O.Agent_Code = A.Agent_Code"; + +var data = new DBContext(DB., ).FetchData(selectSql); +var agent = data.FirstOrDefault(X => X.Agent_Name == "Foo"); +``` +--- +> Transaction + +``` csharp +// Create, Insert & Update +var statements = new List +{ + "CREATE TABLE Employee (EmployeeID INT PRIMARY KEY, FirstName NVARCHAR(50), LastName NVARCHAR(50))", + "INSERT INTO Employee VALUES ('E01', 'John', 'Wick')", + "UPDATE Employee SET FirstName = 'Jack' LastName = 'Reacher' WHERE EmployeeID = 'E01'" +}; +var dbContext = new DBContext(DB.MSSQL, MSSQLConnectionString); +var result = dbContext.ExecuteTransaction(statements); +---