From e431f08556e6fa08f8e285558575e6c045b5e2a4 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Thu, 23 Apr 2020 23:45:31 -0700 Subject: [PATCH 01/34] order hint implementation --- doc/samples/SqlBulkCopy_ColumnOrderHint.cs | 83 +++ .../SqlBulkCopy_ColumnOrderHintCollection.cs | 83 +++ ...qlBulkCopy_ColumnOrderHintCollectionAdd.cs | 86 +++ ...lBulkCopy_ColumnOrderHintCollectionAdd2.cs | 83 +++ ...BulkCopy_ColumnOrderHintCollectionClear.cs | 177 ++++++ ...ulkCopy_ColumnOrderHintCollectionRemove.cs | 176 ++++++ ...kCopy_ColumnOrderHintCollectionRemoveAt.cs | 176 ++++++ .../SqlBulkCopy_ColumnOrderHintColumn.cs | 84 +++ .../SqlBulkCopy_ColumnOrderHintSortOrder.cs | 84 +++ .../SqlBulkCopyColumnOrderHint.xml | 135 +++++ .../SqlBulkCopyColumnOrderHintCollection.xml | 221 ++++++++ src/Microsoft.Data.SqlClient.sln | 16 +- .../src/Microsoft.Data.SqlClient.csproj | 10 +- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 59 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 24 + .../netcore/src/Resources/SR.Designer.cs | 45 ++ .../netcore/src/Resources/SR.resx | 17 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 36 +- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 53 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 24 + .../netfx/src/Resources/Strings.Designer.cs | 45 ++ .../netfx/src/Resources/Strings.resx | 15 + .../SqlClient/SqlBulkCopyColumnOrderHint.cs | 62 +++ .../SqlBulkCopyColumnOrderHintCollection.cs | 84 +++ .../Microsoft.Data.SqlClient.Tests.csproj | 1 + ...qlBulkCopyColumnOrderHintCollectionTest.cs | 508 ++++++++++++++++++ ....Data.SqlClient.ManualTesting.Tests.csproj | 6 + .../SQL/SqlBulkCopyTest/OrderHint.cs | 101 ++++ .../SQL/SqlBulkCopyTest/OrderHintAsync.cs | 108 ++++ .../OrderHintDuplicateColumn.cs | 56 ++ .../OrderHintIdentityColumn.cs | 64 +++ .../OrderHintMissingTargetColumn.cs | 72 +++ .../SqlBulkCopyTest/OrderHintTransaction.cs | 52 ++ .../SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs | 38 ++ 34 files changed, 2857 insertions(+), 27 deletions(-) create mode 100644 doc/samples/SqlBulkCopy_ColumnOrderHint.cs create mode 100644 doc/samples/SqlBulkCopy_ColumnOrderHintCollection.cs create mode 100644 doc/samples/SqlBulkCopy_ColumnOrderHintCollectionAdd.cs create mode 100644 doc/samples/SqlBulkCopy_ColumnOrderHintCollectionAdd2.cs create mode 100644 doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs create mode 100644 doc/samples/SqlBulkCopy_ColumnOrderHintCollectionRemove.cs create mode 100644 doc/samples/SqlBulkCopy_ColumnOrderHintCollectionRemoveAt.cs create mode 100644 doc/samples/SqlBulkCopy_ColumnOrderHintColumn.cs create mode 100644 doc/samples/SqlBulkCopy_ColumnOrderHintSortOrder.cs create mode 100644 doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml create mode 100644 doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHint.cs b/doc/samples/SqlBulkCopy_ColumnOrderHint.cs new file mode 100644 index 0000000000..fd3e003b3a --- /dev/null +++ b/doc/samples/SqlBulkCopy_ColumnOrderHint.cs @@ -0,0 +1,83 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + // Open a sourceConnection to the AdventureWorks database. + using (SqlConnection sourceConnection = + new SqlConnection(connectionString)) + { + sourceConnection.Open(); + + // Perform an initial count on the destination table. + SqlCommand commandRowCount = new SqlCommand( + "SELECT COUNT(*) FROM " + + "dbo.BulkCopyDemoMatchingColumns;", + sourceConnection); + long countStart = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Starting row count = {0}", countStart); + + // Get data from the source table as a SqlDataReader. + SqlCommand commandSourceData = new SqlCommand( + "SELECT ProductID, Name, " + + "ProductNumber " + + "FROM Production.Product;", sourceConnection); + SqlDataReader reader = + commandSourceData.ExecuteReader(); + + // Set up the bulk copy object. + using (SqlBulkCopy bulkCopy = + new SqlBulkCopy(connectionString)) + { + bulkCopy.DestinationTableName = + "dbo.BulkCopyDemoMatchingColumns"; + + // Setup an order hint for the ProductNumber column. + SqlBulkCopyColumnOrderHint hintNumber = + new SqlBulkCopyColumnOrderHint("ProductNumber", SortOrder.Ascending); + bulkCopy.ColumnOrderHints.Add(hintNumber); + + // Write from the source to the destination. + try + { + bulkCopy.WriteToServer(reader); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + // Close the SqlDataReader. The SqlBulkCopy + // object is automatically closed at the end + // of the using block. + reader.Close(); + } + } + + // Perform a final count on the destination + // table to see how many rows were added. + long countEnd = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Ending row count = {0}", countEnd); + Console.WriteLine("{0} rows were added.", countEnd - countStart); + Console.WriteLine("Press Enter to finish."); + Console.ReadLine(); + } + } + + private static string GetConnectionString() + // To avoid storing the sourceConnection string in your code, + // you can retrieve it from a configuration file. + { + return "Data Source=(local); " + + " Integrated Security=true;" + + "Initial Catalog=AdventureWorks;"; + } +} \ No newline at end of file diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintCollection.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintCollection.cs new file mode 100644 index 0000000000..4a92099ddb --- /dev/null +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintCollection.cs @@ -0,0 +1,83 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + // Open a sourceConnection to the AdventureWorks database. + using (SqlConnection sourceConnection = + new SqlConnection(connectionString)) + { + sourceConnection.Open(); + + // Perform an initial count on the destination table. + SqlCommand commandRowCount = new SqlCommand( + "SELECT COUNT(*) FROM " + + "dbo.BulkCopyDemoMatchingColumns;", + sourceConnection); + long countStart = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Starting row count = {0}", countStart); + + // Get data from the source table as a SqlDataReader. + SqlCommand commandSourceData = new SqlCommand( + "SELECT ProductID, Name, " + + "ProductNumber " + + "FROM Production.Product;", sourceConnection); + SqlDataReader reader = + commandSourceData.ExecuteReader(); + + // Set up the bulk copy object. + using (SqlBulkCopy bulkCopy = + new SqlBulkCopy(connectionString)) + { + bulkCopy.DestinationTableName = + "dbo.BulkCopyDemoMatchingColumns"; + + // Specify the sort order for the ProductNumber column in + // the destination table. + bulkCopy.ColumnOrderHints.Add("ProductNumber", SortOrder.Ascending); + + // Write from the source to the destination. + try + { + bulkCopy.WriteToServer(reader); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + // Close the SqlDataReader. The SqlBulkCopy + // object is automatically closed at the end + // of the using block. + reader.Close(); + } + } + + // Perform a final count on the destination + // table to see how many rows were added. + long countEnd = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Ending row count = {0}", countEnd); + Console.WriteLine("{0} rows were added.", countEnd - countStart); + Console.WriteLine("Press Enter to finish."); + Console.ReadLine(); + } + } + + private static string GetConnectionString() + // To avoid storing the sourceConnection string in your code, + // you can retrieve it from a configuration file. + { + return "Data Source=(local); " + + " Integrated Security=true;" + + "Initial Catalog=AdventureWorks;"; + } +} +// diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionAdd.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionAdd.cs new file mode 100644 index 0000000000..2d6599d525 --- /dev/null +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionAdd.cs @@ -0,0 +1,86 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + // Open a sourceConnection to the AdventureWorks database. + using (SqlConnection sourceConnection = + new SqlConnection(connectionString)) + { + sourceConnection.Open(); + + // Perform an initial count on the destination table. + SqlCommand commandRowCount = new SqlCommand( + "SELECT COUNT(*) FROM " + + "dbo.BulkCopyDemoMatchingColumns;", + sourceConnection); + long countStart = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Starting row count = {0}", countStart); + + // Get data from the source table as a SqlDataReader. + SqlCommand commandSourceData = new SqlCommand( + "SELECT ProductID, Name, " + + "ProductNumber " + + "FROM Production.Product;", sourceConnection); + SqlDataReader reader = + commandSourceData.ExecuteReader(); + + // Set up the bulk copy object. + using (SqlBulkCopy bulkCopy = + new SqlBulkCopy(connectionString)) + { + bulkCopy.DestinationTableName = + "dbo.BulkCopyDemoMatchingColumns"; + + // Specify the sort order for the ProductNumber column in + // the destination table. + // Setup an order hint for the ProductNumber column. + SqlBulkCopyColumnOrderHint hintNumber = + new SqlBulkCopyColumnOrderHint("ProductNumber", SortOrder.Ascending); + bulkCopy.ColumnOrderHints.Add(hintNumber); + + // Write from the source to the destination. + try + { + bulkCopy.WriteToServer(reader); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + // Close the SqlDataReader. The SqlBulkCopy + // object is automatically closed at the end + // of the using block. + reader.Close(); + } + } + + // Perform a final count on the destination + // table to see how many rows were added. + long countEnd = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Ending row count = {0}", countEnd); + Console.WriteLine("{0} rows were added.", countEnd - countStart); + Console.WriteLine("Press Enter to finish."); + Console.ReadLine(); + } + } + + private static string GetConnectionString() + // To avoid storing the sourceConnection string in your code, + // you can retrieve it from a configuration file. + { + return "Data Source=(local); " + + " Integrated Security=true;" + + "Initial Catalog=AdventureWorks;"; + } +} +// diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionAdd2.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionAdd2.cs new file mode 100644 index 0000000000..4a92099ddb --- /dev/null +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionAdd2.cs @@ -0,0 +1,83 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + // Open a sourceConnection to the AdventureWorks database. + using (SqlConnection sourceConnection = + new SqlConnection(connectionString)) + { + sourceConnection.Open(); + + // Perform an initial count on the destination table. + SqlCommand commandRowCount = new SqlCommand( + "SELECT COUNT(*) FROM " + + "dbo.BulkCopyDemoMatchingColumns;", + sourceConnection); + long countStart = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Starting row count = {0}", countStart); + + // Get data from the source table as a SqlDataReader. + SqlCommand commandSourceData = new SqlCommand( + "SELECT ProductID, Name, " + + "ProductNumber " + + "FROM Production.Product;", sourceConnection); + SqlDataReader reader = + commandSourceData.ExecuteReader(); + + // Set up the bulk copy object. + using (SqlBulkCopy bulkCopy = + new SqlBulkCopy(connectionString)) + { + bulkCopy.DestinationTableName = + "dbo.BulkCopyDemoMatchingColumns"; + + // Specify the sort order for the ProductNumber column in + // the destination table. + bulkCopy.ColumnOrderHints.Add("ProductNumber", SortOrder.Ascending); + + // Write from the source to the destination. + try + { + bulkCopy.WriteToServer(reader); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + // Close the SqlDataReader. The SqlBulkCopy + // object is automatically closed at the end + // of the using block. + reader.Close(); + } + } + + // Perform a final count on the destination + // table to see how many rows were added. + long countEnd = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Ending row count = {0}", countEnd); + Console.WriteLine("{0} rows were added.", countEnd - countStart); + Console.WriteLine("Press Enter to finish."); + Console.ReadLine(); + } + } + + private static string GetConnectionString() + // To avoid storing the sourceConnection string in your code, + // you can retrieve it from a configuration file. + { + return "Data Source=(local); " + + " Integrated Security=true;" + + "Initial Catalog=AdventureWorks;"; + } +} +// diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs new file mode 100644 index 0000000000..066ecf5c2c --- /dev/null +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs @@ -0,0 +1,177 @@ +using System; +using System.Data; +// +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + // Open a connection to the AdventureWorks database. + using (SqlConnection connection = + new SqlConnection(connectionString)) + { + connection.Open(); + + // Empty the destination tables. + SqlCommand deleteHeader = new SqlCommand( + "DELETE FROM dbo.BulkCopyDemoOrderHeader;", + connection); + deleteHeader.ExecuteNonQuery(); + SqlCommand deleteDetail = new SqlCommand( + "DELETE FROM dbo.BulkCopyDemoOrderDetail;", + connection); + deleteDetail.ExecuteNonQuery(); + + // Perform an initial count on the destination + // table with matching columns. + SqlCommand countRowHeader = new SqlCommand( + "SELECT COUNT(*) FROM dbo.BulkCopyDemoOrderHeader;", + connection); + long countStartHeader = System.Convert.ToInt32( + countRowHeader.ExecuteScalar()); + Console.WriteLine( + "Starting row count for Header table = {0}", + countStartHeader); + + // Perform an initial count on the destination + // table with different column positions. + SqlCommand countRowDetail = new SqlCommand( + "SELECT COUNT(*) FROM dbo.BulkCopyDemoOrderDetail;", + connection); + long countStartDetail = System.Convert.ToInt32( + countRowDetail.ExecuteScalar()); + Console.WriteLine( + "Starting row count for Detail table = {0}", + countStartDetail); + + // Get data from the source table as a SqlDataReader. + // The Sales.SalesOrderHeader and Sales.SalesOrderDetail + // tables are quite large and could easily cause a timeout + // if all data from the tables is added to the destination. + // To keep the example simple and quick, a parameter is + // used to select only orders for a particular account + // as the source for the bulk insert. + SqlCommand headerData = new SqlCommand( + "SELECT [SalesOrderID], [OrderDate], " + + "[AccountNumber] FROM [Sales].[SalesOrderHeader] " + + "WHERE [AccountNumber] = @accountNumber;", + connection); + SqlParameter parameterAccount = new SqlParameter(); + parameterAccount.ParameterName = "@accountNumber"; + parameterAccount.SqlDbType = SqlDbType.NVarChar; + parameterAccount.Direction = ParameterDirection.Input; + parameterAccount.Value = "10-4020-000034"; + headerData.Parameters.Add(parameterAccount); + SqlDataReader readerHeader = headerData.ExecuteReader(); + + // Get the Detail data in a separate connection. + using (SqlConnection connection2 = new SqlConnection(connectionString)) + { + connection2.Open(); + SqlCommand sourceDetailData = new SqlCommand( + "SELECT [Sales].[SalesOrderDetail].[SalesOrderID], [SalesOrderDetailID], " + + "[OrderQty], [ProductID], [UnitPrice] FROM [Sales].[SalesOrderDetail] " + + "INNER JOIN [Sales].[SalesOrderHeader] ON [Sales].[SalesOrderDetail]." + + "[SalesOrderID] = [Sales].[SalesOrderHeader].[SalesOrderID] " + + "WHERE [AccountNumber] = @accountNumber;", connection2); + + SqlParameter accountDetail = new SqlParameter(); + accountDetail.ParameterName = "@accountNumber"; + accountDetail.SqlDbType = SqlDbType.NVarChar; + accountDetail.Direction = ParameterDirection.Input; + accountDetail.Value = "10-4020-000034"; + sourceDetailData.Parameters.Add(accountDetail); + SqlDataReader readerDetail = sourceDetailData.ExecuteReader(); + + // Create the SqlBulkCopy object. + using (SqlBulkCopy bulkCopy = + new SqlBulkCopy(connectionString)) + { + bulkCopy.DestinationTableName = + "dbo.BulkCopyDemoOrderHeader"; + + // Guarantee that columns are mapped correctly by + // defining the column mappings for the order. + bulkCopy.ColumnMappings.Add("SalesOrderID", "SalesOrderID"); + bulkCopy.ColumnMappings.Add("OrderDate", "OrderDate"); + bulkCopy.ColumnMappings.Add("AccountNumber", "AccountNumber"); + + // Add order hint for OrderDate column. + bulkCopy.ColumnOrderHints.Add("OrderDate", SortOrder.Ascending); + + // Write readerHeader to the destination. + try + { + bulkCopy.WriteToServer(readerHeader); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + readerHeader.Close(); + } + + // Set up the order details destination. + bulkCopy.DestinationTableName = "dbo.BulkCopyDemoOrderDetail"; + + // Clear the ColumnMappingCollection. + bulkCopy.ColumnMappings.Clear(); + + // Add order detail column mappings. + bulkCopy.ColumnMappings.Add("SalesOrderID", "SalesOrderID"); + bulkCopy.ColumnMappings.Add("SalesOrderDetailID", "SalesOrderDetailID"); + bulkCopy.ColumnMappings.Add("OrderQty", "OrderQty"); + bulkCopy.ColumnMappings.Add("ProductID", "ProductID"); + bulkCopy.ColumnMappings.Add("UnitPrice", "UnitPrice"); + + // Clear the ColumnOrderHintCollection. + bulkCopy.ColumnOrderHints.Clear(); + + // Add order hint for SalesOrderID column. + bulkCopy.ColumnOrderHints.Add("SalesOrderID", SortOrder.Ascending); + + // Write readerDetail to the destination. + try + { + bulkCopy.WriteToServer(readerDetail); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + readerDetail.Close(); + } + } + + // Perform a final count on the destination + // tables to see how many rows were added. + long countEndHeader = System.Convert.ToInt32( + countRowHeader.ExecuteScalar()); + Console.WriteLine("{0} rows were added to the Header table.", + countEndHeader - countStartHeader); + long countEndDetail = System.Convert.ToInt32( + countRowDetail.ExecuteScalar()); + Console.WriteLine("{0} rows were added to the Detail table.", + countEndDetail - countStartDetail); + Console.WriteLine("Press Enter to finish."); + Console.ReadLine(); + } + } + } + + private static string GetConnectionString() + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + { + return "Data Source=(local); " + + " Integrated Security=true;" + + "Initial Catalog=AdventureWorks;"; + } +} +// diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionRemove.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionRemove.cs new file mode 100644 index 0000000000..ea9a8f4a46 --- /dev/null +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionRemove.cs @@ -0,0 +1,176 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + // Open a connection to the AdventureWorks database. + using (SqlConnection connection = + new SqlConnection(connectionString)) + { + connection.Open(); + + // Empty the destination tables. + SqlCommand deleteHeader = new SqlCommand( + "DELETE FROM dbo.BulkCopyDemoOrderHeader;", + connection); + deleteHeader.ExecuteNonQuery(); + SqlCommand deleteDetail = new SqlCommand( + "DELETE FROM dbo.BulkCopyDemoOrderDetail;", + connection); + deleteDetail.ExecuteNonQuery(); + + // Perform an initial count on the destination + // table with matching columns. + SqlCommand countRowHeader = new SqlCommand( + "SELECT COUNT(*) FROM dbo.BulkCopyDemoOrderHeader;", + connection); + long countStartHeader = System.Convert.ToInt32( + countRowHeader.ExecuteScalar()); + Console.WriteLine( + "Starting row count for Header table = {0}", + countStartHeader); + + // Perform an initial count on the destination + // table with different column positions. + SqlCommand countRowDetail = new SqlCommand( + "SELECT COUNT(*) FROM dbo.BulkCopyDemoOrderDetail;", + connection); + long countStartDetail = System.Convert.ToInt32( + countRowDetail.ExecuteScalar()); + Console.WriteLine( + "Starting row count for Detail table = {0}", + countStartDetail); + + // Get data from the source table as a SqlDataReader. + // The Sales.SalesOrderHeader and Sales.SalesOrderDetail + // tables are quite large and could easily cause a timeout + // if all data from the tables is added to the destination. + // To keep the example simple and quick, a parameter is + // used to select only orders for a particular account + // as the source for the bulk insert. + SqlCommand headerData = new SqlCommand( + "SELECT [SalesOrderID], [OrderDate], " + + "[AccountNumber] FROM [Sales].[SalesOrderHeader] " + + "WHERE [AccountNumber] = @accountNumber;", + connection); + SqlParameter parameterAccount = new SqlParameter(); + parameterAccount.ParameterName = "@accountNumber"; + parameterAccount.SqlDbType = SqlDbType.NVarChar; + parameterAccount.Direction = ParameterDirection.Input; + parameterAccount.Value = "10-4020-000034"; + headerData.Parameters.Add(parameterAccount); + SqlDataReader readerHeader = headerData.ExecuteReader(); + + // Get the Detail data in a separate connection. + using (SqlConnection connection2 = new SqlConnection(connectionString)) + { + connection2.Open(); + SqlCommand sourceDetailData = new SqlCommand( + "SELECT [Sales].[SalesOrderDetail].[SalesOrderID], [SalesOrderDetailID], " + + "[OrderQty], [ProductID], [UnitPrice] FROM [Sales].[SalesOrderDetail] " + + "INNER JOIN [Sales].[SalesOrderHeader] ON [Sales].[SalesOrderDetail]." + + "[SalesOrderID] = [Sales].[SalesOrderHeader].[SalesOrderID] " + + "WHERE [AccountNumber] = @accountNumber;", connection2); + + SqlParameter accountDetail = new SqlParameter(); + accountDetail.ParameterName = "@accountNumber"; + accountDetail.SqlDbType = SqlDbType.NVarChar; + accountDetail.Direction = ParameterDirection.Input; + accountDetail.Value = "10-4020-000034"; + sourceDetailData.Parameters.Add(accountDetail); + SqlDataReader readerDetail = sourceDetailData.ExecuteReader(); + + // Create the SqlBulkCopy object. + using (SqlBulkCopy bulkCopy = + new SqlBulkCopy(connectionString)) + { + bulkCopy.DestinationTableName = + "dbo.BulkCopyDemoOrderHeader"; + + // Guarantee that columns are mapped correctly by + // defining the column mappings for the order. + bulkCopy.ColumnMappings.Add("SalesOrderID", "SalesOrderID"); + bulkCopy.ColumnMappings.Add("OrderDate", "OrderDate"); + bulkCopy.ColumnMappings.Add("AccountNumber", "AccountNumber"); + + // Add the order hint for the OrderDate column. + SqlBulkCopyColumnOrderHint hintDate = + new SqlBulkCopyColumnOrderHint("OrderDate", SortOrder.Ascending); + bulkCopy.ColumnOrderHints.Add(hintDate); + + // Write readerHeader to the destination. + try + { + bulkCopy.WriteToServer(readerHeader); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + readerHeader.Close(); + } + + // Set up the order details destination. + bulkCopy.DestinationTableName = "dbo.BulkCopyDemoOrderDetail"; + + // Clear the ColumnMappingCollection. + bulkCopy.ColumnMappings.Clear(); + + // Add order detail column mappings. + bulkCopy.ColumnMappings.Add("SalesOrderID", "SalesOrderID"); + bulkCopy.ColumnMappings.Add("SalesOrderDetailID", "SalesOrderDetailID"); + bulkCopy.ColumnMappings.Add("OrderQty", "OrderQty"); + bulkCopy.ColumnMappings.Add("ProductID", "ProductID"); + bulkCopy.ColumnMappings.Add("UnitPrice", "UnitPrice"); + + // Remove the order hint for the OrderDate column. + bulkCopy.ColumnOrderHints.Remove(hintDate); + + // Write readerDetail to the destination. + try + { + bulkCopy.WriteToServer(readerDetail); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + readerDetail.Close(); + } + } + + // Perform a final count on the destination + // tables to see how many rows were added. + long countEndHeader = System.Convert.ToInt32( + countRowHeader.ExecuteScalar()); + Console.WriteLine("{0} rows were added to the Header table.", + countEndHeader - countStartHeader); + long countEndDetail = System.Convert.ToInt32( + countRowDetail.ExecuteScalar()); + Console.WriteLine("{0} rows were added to the Detail table.", + countEndDetail - countStartDetail); + Console.WriteLine("Press Enter to finish."); + Console.ReadLine(); + } + } + } + + private static string GetConnectionString() + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + { + return "Data Source=(local); " + + " Integrated Security=true;" + + "Initial Catalog=AdventureWorks;"; + } +} +// diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionRemoveAt.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionRemoveAt.cs new file mode 100644 index 0000000000..b965599175 --- /dev/null +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionRemoveAt.cs @@ -0,0 +1,176 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + // Open a connection to the AdventureWorks database. + using (SqlConnection connection = + new SqlConnection(connectionString)) + { + connection.Open(); + + // Empty the destination tables. + SqlCommand deleteHeader = new SqlCommand( + "DELETE FROM dbo.BulkCopyDemoOrderHeader;", + connection); + deleteHeader.ExecuteNonQuery(); + SqlCommand deleteDetail = new SqlCommand( + "DELETE FROM dbo.BulkCopyDemoOrderDetail;", + connection); + deleteDetail.ExecuteNonQuery(); + + // Perform an initial count on the destination + // table with matching columns. + SqlCommand countRowHeader = new SqlCommand( + "SELECT COUNT(*) FROM dbo.BulkCopyDemoOrderHeader;", + connection); + long countStartHeader = System.Convert.ToInt32( + countRowHeader.ExecuteScalar()); + Console.WriteLine( + "Starting row count for Header table = {0}", + countStartHeader); + + // Perform an initial count on the destination + // table with different column positions. + SqlCommand countRowDetail = new SqlCommand( + "SELECT COUNT(*) FROM dbo.BulkCopyDemoOrderDetail;", + connection); + long countStartDetail = System.Convert.ToInt32( + countRowDetail.ExecuteScalar()); + Console.WriteLine( + "Starting row count for Detail table = {0}", + countStartDetail); + + // Get data from the source table as a SqlDataReader. + // The Sales.SalesOrderHeader and Sales.SalesOrderDetail + // tables are quite large and could easily cause a timeout + // if all data from the tables is added to the destination. + // To keep the example simple and quick, a parameter is + // used to select only orders for a particular account + // as the source for the bulk insert. + SqlCommand headerData = new SqlCommand( + "SELECT [SalesOrderID], [OrderDate], " + + "[AccountNumber] FROM [Sales].[SalesOrderHeader] " + + "WHERE [AccountNumber] = @accountNumber;", + connection); + SqlParameter parameterAccount = new SqlParameter(); + parameterAccount.ParameterName = "@accountNumber"; + parameterAccount.SqlDbType = SqlDbType.NVarChar; + parameterAccount.Direction = ParameterDirection.Input; + parameterAccount.Value = "10-4020-000034"; + headerData.Parameters.Add(parameterAccount); + SqlDataReader readerHeader = headerData.ExecuteReader(); + + // Get the Detail data in a separate connection. + using (SqlConnection connection2 = new SqlConnection(connectionString)) + { + connection2.Open(); + SqlCommand sourceDetailData = new SqlCommand( + "SELECT [Sales].[SalesOrderDetail].[SalesOrderID], [SalesOrderDetailID], " + + "[OrderQty], [ProductID], [UnitPrice] FROM [Sales].[SalesOrderDetail] " + + "INNER JOIN [Sales].[SalesOrderHeader] ON [Sales].[SalesOrderDetail]." + + "[SalesOrderID] = [Sales].[SalesOrderHeader].[SalesOrderID] " + + "WHERE [AccountNumber] = @accountNumber;", connection2); + + SqlParameter accountDetail = new SqlParameter(); + accountDetail.ParameterName = "@accountNumber"; + accountDetail.SqlDbType = SqlDbType.NVarChar; + accountDetail.Direction = ParameterDirection.Input; + accountDetail.Value = "10-4020-000034"; + sourceDetailData.Parameters.Add(accountDetail); + SqlDataReader readerDetail = sourceDetailData.ExecuteReader(); + + // Create the SqlBulkCopy object. + using (SqlBulkCopy bulkCopy = + new SqlBulkCopy(connectionString)) + { + bulkCopy.DestinationTableName = + "dbo.BulkCopyDemoOrderHeader"; + + // Guarantee that columns are mapped correctly by + // defining the column mappings for the order. + bulkCopy.ColumnMappings.Add("SalesOrderID", "SalesOrderID"); + bulkCopy.ColumnMappings.Add("OrderDate", "OrderDate"); + bulkCopy.ColumnMappings.Add("AccountNumber", "AccountNumber"); + + // Add the order hint for the OrderDate column. + SqlBulkCopyColumnOrderHint hintDate = + new SqlBulkCopyColumnOrderHint("OrderDate", SortOrder.Ascending); + bulkCopy.ColumnOrderHints.Add(hintDate); + + // Write readerHeader to the destination. + try + { + bulkCopy.WriteToServer(readerHeader); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + readerHeader.Close(); + } + + // Set up the order details destination. + bulkCopy.DestinationTableName = "dbo.BulkCopyDemoOrderDetail"; + + // Clear the ColumnMappingCollection. + bulkCopy.ColumnMappings.Clear(); + + // Add order detail column mappings. + bulkCopy.ColumnMappings.Add("SalesOrderID", "SalesOrderID"); + bulkCopy.ColumnMappings.Add("SalesOrderDetailID", "SalesOrderDetailID"); + bulkCopy.ColumnMappings.Add("OrderQty", "OrderQty"); + bulkCopy.ColumnMappings.Add("ProductID", "ProductID"); + bulkCopy.ColumnMappings.Add("UnitPrice", "UnitPrice"); + + // Remove the order hint for the OrderDate column. + bulkCopy.ColumnOrderHints.RemoveAt(0); + + // Write readerDetail to the destination. + try + { + bulkCopy.WriteToServer(readerDetail); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + readerDetail.Close(); + } + } + + // Perform a final count on the destination + // tables to see how many rows were added. + long countEndHeader = System.Convert.ToInt32( + countRowHeader.ExecuteScalar()); + Console.WriteLine("{0} rows were added to the Header table.", + countEndHeader - countStartHeader); + long countEndDetail = System.Convert.ToInt32( + countRowDetail.ExecuteScalar()); + Console.WriteLine("{0} rows were added to the Detail table.", + countEndDetail - countStartDetail); + Console.WriteLine("Press Enter to finish."); + Console.ReadLine(); + } + } + } + + private static string GetConnectionString() + // To avoid storing the connection string in your code, + // you can retrieve it from a configuration file. + { + return "Data Source=(local); " + + " Integrated Security=true;" + + "Initial Catalog=AdventureWorks;"; + } +} +// diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintColumn.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintColumn.cs new file mode 100644 index 0000000000..963d5496d2 --- /dev/null +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintColumn.cs @@ -0,0 +1,84 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + // Open a sourceConnection to the AdventureWorks database. + using (SqlConnection sourceConnection = + new SqlConnection(connectionString)) + { + sourceConnection.Open(); + + // Perform an initial count on the destination table. + SqlCommand commandRowCount = new SqlCommand( + "SELECT COUNT(*) FROM " + + "dbo.BulkCopyDemoMatchingColumns;", + sourceConnection); + long countStart = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Starting row count = {0}", countStart); + + // Get data from the source table as a SqlDataReader. + SqlCommand commandSourceData = new SqlCommand( + "SELECT ProductID, Name, " + + "ProductNumber " + + "FROM Production.Product;", sourceConnection); + SqlDataReader reader = + commandSourceData.ExecuteReader(); + + // Set up the bulk copy object. + using (SqlBulkCopy bulkCopy = + new SqlBulkCopy(connectionString)) + { + bulkCopy.DestinationTableName = + "dbo.BulkCopyDemoMatchingColumns"; + + // Setup an order hint for the ProductNumber column. + SqlBulkCopyColumnOrderHint hintNumber = + new SqlBulkCopyColumnOrderHint("number", SortOrder.Ascending); + hintNumber.Column = "ProductNumber"; + bulkCopy.ColumnOrderHints.Add(hintNumber); + + // Write from the source to the destination. + try + { + bulkCopy.WriteToServer(reader); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + // Close the SqlDataReader. The SqlBulkCopy + // object is automatically closed at the end + // of the using block. + reader.Close(); + } + } + + // Perform a final count on the destination + // table to see how many rows were added. + long countEnd = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Ending row count = {0}", countEnd); + Console.WriteLine("{0} rows were added.", countEnd - countStart); + Console.WriteLine("Press Enter to finish."); + Console.ReadLine(); + } + } + + private static string GetConnectionString() + // To avoid storing the sourceConnection string in your code, + // you can retrieve it from a configuration file. + { + return "Data Source=(local); " + + " Integrated Security=true;" + + "Initial Catalog=AdventureWorks;"; + } +} \ No newline at end of file diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintSortOrder.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintSortOrder.cs new file mode 100644 index 0000000000..c83f048012 --- /dev/null +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintSortOrder.cs @@ -0,0 +1,84 @@ +// +using System; +using System.Data; +using Microsoft.Data.SqlClient; + +class Program +{ + static void Main() + { + string connectionString = GetConnectionString(); + // Open a sourceConnection to the AdventureWorks database. + using (SqlConnection sourceConnection = + new SqlConnection(connectionString)) + { + sourceConnection.Open(); + + // Perform an initial count on the destination table. + SqlCommand commandRowCount = new SqlCommand( + "SELECT COUNT(*) FROM " + + "dbo.BulkCopyDemoMatchingColumns;", + sourceConnection); + long countStart = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Starting row count = {0}", countStart); + + // Get data from the source table as a SqlDataReader. + SqlCommand commandSourceData = new SqlCommand( + "SELECT ProductID, Name, " + + "ProductNumber " + + "FROM Production.Product;", sourceConnection); + SqlDataReader reader = + commandSourceData.ExecuteReader(); + + // Set up the bulk copy object. + using (SqlBulkCopy bulkCopy = + new SqlBulkCopy(connectionString)) + { + bulkCopy.DestinationTableName = + "dbo.BulkCopyDemoMatchingColumns"; + + // Setup an order hint for the ProductNumber column. + SqlBulkCopyColumnOrderHint hintNumber = + new SqlBulkCopyColumnOrderHint("ProductNumber", SortOrder.Ascending); + hintNumber.SortOrder = SortOrder.Descending; + bulkCopy.ColumnOrderHints.Add(hintNumber); + + // Write from the source to the destination. + try + { + bulkCopy.WriteToServer(reader); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + // Close the SqlDataReader. The SqlBulkCopy + // object is automatically closed at the end + // of the using block. + reader.Close(); + } + } + + // Perform a final count on the destination + // table to see how many rows were added. + long countEnd = System.Convert.ToInt32( + commandRowCount.ExecuteScalar()); + Console.WriteLine("Ending row count = {0}", countEnd); + Console.WriteLine("{0} rows were added.", countEnd - countStart); + Console.WriteLine("Press Enter to finish."); + Console.ReadLine(); + } + } + + private static string GetConnectionString() + // To avoid storing the sourceConnection string in your code, + // you can retrieve it from a configuration file. + { + return "Data Source=(local); " + + " Integrated Security=true;" + + "Initial Catalog=AdventureWorks;"; + } +} \ No newline at end of file diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml new file mode 100644 index 0000000000..6e376d698b --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml @@ -0,0 +1,135 @@ + + + + + Defines the sort order for a column in a + + instance's destination table, according to the clustered index on the table. + + + collection is not empty, order hints can only be provided for valid +destination columns which have been mapped. + +If a of Unspecified is given, an will be thrown. + +## Examples +The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. +A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. + +> [!IMPORTANT] +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). +This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a +Transact-SQL `INSERT … SELECT` statement to copy the data. + +[!code-csharp[SqlBulkCopy.ColumnOrderHint#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHint.cs#1)] +]]> + + + + + + + The name of the destination column within the destination table. + + + The sort order of the corresponding destination column. + + + Creates a new column order hint for the specified destination column. + + + [!IMPORTANT] +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). +This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a +Transact-SQL `INSERT … SELECT` statement to copy the data. + +[!code-csharp[SqlBulkCopy.ColumnOrderHint#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHint.cs#1)] +]]> + + + + + + Name of the destination column in the destination table for which the hint is being provided. + + + The string value of the + + property. + + + will be thrown if a null or empty string is given. + +The last value set takes precedence. + +## Examples +The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. +A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. + +> [!IMPORTANT] +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). +This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a +Transact-SQL `INSERT … SELECT` statement to copy the data. + +[!code-csharp[SqlBulkCopy.ColumnOrderHintColumn#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHintColumn.cs#1)] +]]> + + + + + + The sort order of the destination column in the destination table. + + + The SortOrder value of the + + property. + + + will be thrown if a of Unspecified is given. + +The last value set takes precedence. + +## Examples +The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. +A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. + +> [!IMPORTANT] +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). +This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a +Transact-SQL `INSERT … SELECT` statement to copy the data. + +[!code-csharp[SqlBulkCopy.ColumnOrderHintSortOrder#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHintSortOrder.cs#1)] +]]> + + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml new file mode 100644 index 0000000000..04a95c0281 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -0,0 +1,221 @@ + + + + Collection of objects that inherits from . + + collection is not empty, order hints can only be provided for valid +destination columns which have been mapped. + +If a of Unspecified is given, an will be thrown. + +## Examples + +The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. + are added to the for the + object to specify order hints for the bulk copy. + +> [!IMPORTANT] +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). +This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to +use a Transact-SQL `INSERT … SELECT` statement to copy the data. + +[!code-csharp[SqlBulkCopy.ColumnOrderHintCollection#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHintCollection.cs#1)] + +]]> + + + + + The object that describes the order hint to be added to the collection. + Adds the specified order hint to the . + A object. + + [!IMPORTANT] +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). +This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, +it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to copy the data. + +[!code-csharp[SqlBulkCopy.ColumnOrderHintCollectionAdd#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionAdd.cs)] + +]]> + + + + + + The name of the destination column within the destination table. + The sort order of the corresponding destination column. + Creates a new and adds it to the collection. + A column column order hint. + + by providing the destination column name and its sort order. + +> [!IMPORTANT] +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). +This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a +Transact-SQL `INSERT … SELECT` statement to copy the data. + +[!code-csharp[SqlBulkCopy.ColumnOrderHintCollectionAdd2#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionAdd2.cs#1)] + +]]> + + + + + Clears the contents of the collection. + + method is most commonly used when you use a single +instance to process more than one bulk copy operation. If you create column order hints for one bulk copy operation, you must clear the + after the method and before processing the next bulk copy. + +Performing several bulk copies using the same instance will usually be more efficient from a performance point of view than using a separate + for each operation. + +## Examples +The following example performs two bulk copy operations. The first operation copies sales order header information, and the second copies sales order details. +The example defines a column order hint for each bulk copy operation. +The method must be used after the first bulk copy is performed and before the next bulk copy's order hint is defined. + +> [!IMPORTANT] +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). +This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a +Transact-SQL `INSERT … SELECT` statement to copy the data. + +[!code-csharp[SqlBulkCopy.ColumnOrderHintCollectionClear#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs#1)] + +]]> + + + + A valid object. + Gets a value indicating whether a specified object exists in the collection. + + if the specified column order hint exists in the collection; otherwise . + + + The one-dimensional array that is the destination of the elements copied from + . The array must have zero-based indexing. + The zero-based index in at which copying begins. + Copies the elements of the to an array of + items, starting at a particular index. + + + + The object for which to search. + Gets the index of the specified object. + The zero-based index of the column order hint, or -1 if the column order hint is not found in the collection. + To be added. + + + Integer value of the location within the at which to insert the new + . + + object to be inserted in the collection. + Insert a new at the index specified. + The order in which column order hints can be added is arbitrary. + + + The zero-based index of the to find. + Gets the object at the specified index. + A object. + To be added. + + + + object to be removed from the collection. + Removes the specified element from the . + + method is most commonly used when you use a single +instance to process more than one bulk copy operation. If you create column order hints for one bulk copy operation, you must clear the + after the method and before processing the next bulk copy. +You can clear the entire collection by using the + method, or remove hints individually using the +method or the method. + +Performing several bulk copies using the same instance will usually be more efficient from a performance point of view than using a separate + for each operation. + +## Examples +The following example performs two bulk copy operations. The first operation copies sales order header information, and the second copies sales order details. +The example defines a column order hint for the **OrderDate** column in the first bulk copy operation. The hint is removed before the second bulk copy operation. + +> [!IMPORTANT] +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). +This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a +Transact-SQL `INSERT … SELECT` statement to copy the data. + +[!code-csharp[SqlBulkCopy.ColumnOrderHintCollectionRemove#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionRemove.cs#1)] + +]]> + + + + The zero-based index of the object to be removed from the collection. + Removes the column order hint at the specified index from the collection. + + method is most commonly used when you use a single +instance to process more than one bulk copy operation. If you create column order hints for one bulk copy operation, you must clear the + after the method and before processing the next bulk copy. +You can clear the entire collection by using the + method, or remove hints individually using the +method or the method. + +Performing several bulk copies using the same instance will usually be more efficient from a performance point of view than using a separate + for each operation. + +## Examples +The following example performs two bulk copy operations. The first operation copies sales order header information, and the second copies sales order details. +The example defines a column order hint for the **OrderDate** column in the first bulk copy operation. The hint is removed before the second bulk copy operation. + +> [!IMPORTANT] +> This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). +This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. If the source and destination tables are in the same SQL Server instance, it is easier and faster to use a +Transact-SQL `INSERT … SELECT` statement to copy the data. + +[!code-csharp[SqlBulkCopy.ColumnOrderHintCollectionRemoveAt#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionRemoveAt.cs#1)] + +]]> + + + + Gets a value indicating whether access to the is synchronized (thread safe). + `true` if access to the is synchronized (thread safe); otherwise, `false`. + + + diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 6c9a8f59b8..0da3b93aa9 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -767,16 +767,16 @@ Global {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp2.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp2.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.Build.0 = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x64.ActiveCfg = Release|Any CPU diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 102d6325b1..826f375a71 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -33,7 +33,7 @@ Microsoft\Data\SqlClient\SqlClientEventSource.cs - + Microsoft\Data\SqlClient\SqlClientLogger.cs @@ -57,7 +57,7 @@ Microsoft\Data\SqlClient\ColumnEncryptionKeyInfo.cs - + Microsoft\Data\SqlClient\OnChangedEventHandler.cs @@ -84,6 +84,12 @@ Microsoft\Data\SqlClient\SqlBulkCopyOptions.cs + + Microsoft\Data\SqlClient\SqlBulkCopyColumnOrderHint.cs + + + Microsoft\Data\SqlClient\SqlBulkCopyColumnOrderHintCollection.cs + Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithmFactory.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 686e194397..f92a6eaa07 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -218,13 +218,13 @@ private int RowNumber rowNo = ((DataTable)_dataTableSource).Rows.IndexOf(_rowEnumerator.Current as DataRow); break; case ValueSourceType.DataTable: - rowNo = ((DataTable)_rowSource).Rows.IndexOf(_rowEnumerator.Current as DataRow); + rowNo = ((DataTable)_rowSource).Rows.IndexOf(_rowEnumerator.Current as DataRow); break; case ValueSourceType.DbDataReader: case ValueSourceType.IDataReader: case ValueSourceType.Unspecified: default: - return -1; + return -1; } return ++rowNo; } @@ -233,6 +233,7 @@ private int RowNumber private TdsParser _parser; private TdsParserStateObject _stateObj; private List<_ColumnMapping> _sortedColumnMappings; + private HashSet _destColumnNames; private SqlRowsCopiedEventHandler _rowsCopiedEventHandler; @@ -272,6 +273,7 @@ public SqlBulkCopy(SqlConnection connection) } _connection = connection; _columnMappings = new SqlBulkCopyColumnMappingCollection(); + ColumnOrderHints = new SqlBulkCopyColumnOrderHintCollection(); } /// @@ -299,6 +301,7 @@ public SqlBulkCopy(string connectionString) } _connection = new SqlConnection(connectionString); _columnMappings = new SqlBulkCopyColumnMappingCollection(); + ColumnOrderHints = new SqlBulkCopyColumnOrderHintCollection(); _ownConnection = true; } @@ -368,6 +371,12 @@ public SqlBulkCopyColumnMappingCollection ColumnMappings } } + /// + public SqlBulkCopyColumnOrderHintCollection ColumnOrderHints + { + get; + } + /// public string DestinationTableName { @@ -594,7 +603,6 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i int nmatched = 0; // Number of columns that match and are accepted int nrejected = 0; // Number of columns that match but were rejected bool rejectColumn; // True if a column is rejected because of an excluded type - bool isInTransaction; isInTransaction = _connection.HasLocalTransaction; @@ -604,6 +612,8 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i throw SQL.BulkLoadExistingTransaction(); } + _destColumnNames = new HashSet(); + // Loop over the metadata for each column _SqlMetaDataSet metaDataSet = internalResults[MetaDataResultId].MetaData; _sortedColumnMappings = new List<_ColumnMapping>(metaDataSet.Length); @@ -636,6 +646,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i } _sortedColumnMappings.Add(new _ColumnMapping(_localColumnMappings[assocId]._internalSourceColumnOrdinal, metadata)); + _destColumnNames.Add(metadata.column); nmatched++; if (nmatched > 1) @@ -770,12 +781,13 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i updateBulkCommandText.Append(")"); - if ((_copyOptions & ( + if (((_copyOptions & ( SqlBulkCopyOptions.KeepNulls | SqlBulkCopyOptions.TableLock | SqlBulkCopyOptions.CheckConstraints | SqlBulkCopyOptions.FireTriggers | SqlBulkCopyOptions.AllowEncryptedValueModifications)) != SqlBulkCopyOptions.Default) + || ColumnOrderHints.Count > 0) { bool addSeparator = false; // Insert a comma character if multiple options in list updateBulkCommandText.Append(" with ("); @@ -804,11 +816,49 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i updateBulkCommandText.Append((addSeparator ? ", " : "") + "ALLOW_ENCRYPTED_VALUE_MODIFICATIONS"); addSeparator = true; } + if (ColumnOrderHints.Count > 0) + { + updateBulkCommandText.Append((addSeparator ? ", " : "") + TryGetOrderHintText()); + } updateBulkCommandText.Append(")"); } return (updateBulkCommandText.ToString()); } + private string TryGetOrderHintText() + { + StringBuilder orderHintText = new StringBuilder("ORDER("); + HashSet columnNames = new HashSet(); + + foreach (SqlBulkCopyColumnOrderHint columnOrderHint in ColumnOrderHints) + { + string columnNameArg = columnOrderHint.Column; + + if (!_destColumnNames.Contains(columnNameArg)) + { + // column is not valid in the destination table + throw SQL.BulkLoadOrderHintInvalidColumn(columnNameArg); + } + + if (!columnNames.Contains(columnNameArg)) + { + string columnNameEscaped = SqlServerEscapeHelper.EscapeIdentifier(SqlServerEscapeHelper.EscapeStringAsLiteral(columnNameArg)); + string sortOrderText = columnOrderHint.SortOrder == SortOrder.Descending ? "DESC" : "ASC"; + orderHintText.Append($"{columnNameEscaped} {sortOrderText}, "); + columnNames.Add(columnNameArg); + } + else + { + // duplicate column name + throw SQL.BulkLoadOrderHintDuplicateColumn(columnNameArg); + } + } + + orderHintText.Length = orderHintText.Length - 2; + orderHintText.Append(")"); + return orderHintText.ToString(); + } + private Task SubmitUpdateBulkCommand(string TDSCommand) { SqlClientEventSource.Log.CorrelationTraceEvent(" ObjectID{0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); @@ -1295,7 +1345,6 @@ private void CreateOrValidateConnection(string method) { throw ADP.ConnectionRequired(method); } - if (_ownConnection && _connection.State != ConnectionState.Open) { _connection.Open(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index 4d0029e70a..5e1cdf3df0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -840,6 +840,30 @@ internal static Exception BulkLoadNonMatchingColumnName(string columnName, Excep { return ADP.InvalidOperation(System.SRHelper.GetString(SR.SQL_BulkLoadNonMatchingColumnName, columnName), e); } + internal static Exception BulkLoadNullEmptyColumnName(string paramName) + { + return ADP.Argument(string.Format(System.SRHelper.GetString(SR.SQL_ParameterCannotBeEmpty), paramName)); + } + internal static Exception BulkLoadUnspecifiedSortOrder() + { + return ADP.Argument(System.SRHelper.GetString(SR.SQL_BulkLoadUnspecifiedSortOrder)); + } + internal static Exception BulkLoadInvalidOrderHint() + { + return ADP.Argument(System.SRHelper.GetString(SR.SQL_BulkLoadInvalidOrderHint)); + } + internal static Exception BulkLoadOrderHintInaccessible() + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.SQL_BulkLoadOrderHintInaccessible)); + } + internal static Exception BulkLoadOrderHintInvalidColumn(string columnName) + { + return ADP.InvalidOperation(string.Format(System.SRHelper.GetString(SR.SQL_BulkLoadOrderHintInvalidColumn), columnName)); + } + internal static Exception BulkLoadOrderHintDuplicateColumn(string columnName) + { + return ADP.InvalidOperation(string.Format(System.SRHelper.GetString(SR.SQL_BulkLoadOrderHintDuplicateColumn), columnName)); + } internal static Exception BulkLoadStringTooLong(string tableName, string columnName, string truncatedValue) { return ADP.InvalidOperation(System.SRHelper.GetString(SR.SQL_BulkLoadStringTooLong, tableName, columnName, truncatedValue)); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index 6e66321a54..0288addf8d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -2193,6 +2193,15 @@ internal static string SQL_BulkLoadInvalidOperationInsideEvent { } } + /// + /// Looks up a localized string similar to The given column order hint is not valid.. + /// + internal static string SQL_BulkLoadInvalidOrderHint { + get { + return ResourceManager.GetString("SQL_BulkLoadInvalidOrderHint", resourceCulture); + } + } + /// /// Looks up a localized string similar to Timeout Value '{0}' is less than 0.. /// @@ -2283,6 +2292,33 @@ internal static string SQL_BulkLoadNotAllowDBNull { } } + /// + /// Looks up a localized string similar to The column '{0}' was specified more than once.. + /// + internal static string SQL_BulkLoadOrderHintDuplicateColumn { + get { + return ResourceManager.GetString("SQL_BulkLoadOrderHintDuplicateColumn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The order hint collection is in use and cannot be accessed at this time.. + /// + internal static string SQL_BulkLoadOrderHintInaccessible { + get { + return ResourceManager.GetString("SQL_BulkLoadOrderHintInaccessible", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The sorted column '{0}' is not valid in the destination table.. + /// + internal static string SQL_BulkLoadOrderHintInvalidColumn { + get { + return ResourceManager.GetString("SQL_BulkLoadOrderHintInvalidColumn", resourceCulture); + } + } + /// /// Looks up a localized string similar to Attempt to invoke bulk copy on an object that has a pending operation.. /// @@ -2301,6 +2337,15 @@ internal static string SQL_BulkLoadStringTooLong { } } + /// + /// Looks up a localized string similar to A column order hint cannot have an unspecified sort order.. + /// + internal static string SQL_BulkLoadUnspecifiedSortOrder { + get { + return ResourceManager.GetString("SQL_BulkLoadUnspecifiedSortOrder", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed to instantiate a SqlAuthenticationInitializer with type '{0}'.. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index d4a5797c0e..95559ff614 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1863,4 +1863,19 @@ The given value{0} of type {1} from the data source cannot be converted to type {2} for Column {3} [{4}]. - + + A column order hint cannot have an unspecified sort order. + + + The order hint collection is in use and cannot be accessed at this time. + + + The given column order hint is not valid. + + + The column '{0}' was specified more than once. + + + The sorted column '{0}' is not valid in the destination table. + + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 8000b94997..d8958b4d68 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -90,7 +90,7 @@ Microsoft\Data\SqlClient\SqlClientEventSource.cs - + Microsoft\Data\SqlClient\SqlClientLogger.cs @@ -114,7 +114,7 @@ Microsoft\Data\SqlClient\ColumnEncryptionKeyInfo.cs - + Microsoft\Data\SqlClient\OnChangedEventHandler.cs @@ -141,6 +141,12 @@ Microsoft\Data\SqlClient\SqlBulkCopyOptions.cs + + Microsoft\Data\SqlClient\SqlBulkCopyColumnOrderHint.cs + + + Microsoft\Data\SqlClient\SqlBulkCopyColumnOrderHintCollection.cs + Microsoft\Data\SqlClient\SqlClientEncryptionAlgorithmFactory.cs @@ -240,10 +246,16 @@ - - + + Component + + + Component + - + + Component + @@ -251,7 +263,9 @@ - + + Component + @@ -323,7 +337,9 @@ - + + Component + @@ -377,10 +393,10 @@ - + True True - $(ResxFileName).resx + Strings.resx @@ -436,4 +452,4 @@ - + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index c4c92977d4..ece3e77798 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -291,6 +291,7 @@ private int RowNumber private TdsParser _parser; private TdsParserStateObject _stateObj; private List<_ColumnMapping> _sortedColumnMappings; + private HashSet _destColumnNames; private SqlRowsCopiedEventHandler _rowsCopiedEventHandler; @@ -333,6 +334,7 @@ public SqlBulkCopy(SqlConnection connection) } _connection = connection; _columnMappings = new SqlBulkCopyColumnMappingCollection(); + ColumnOrderHints = new SqlBulkCopyColumnOrderHintCollection(); } /// @@ -361,6 +363,7 @@ public SqlBulkCopy(string connectionString) : this(new SqlConnection(connectionS } _connection = new SqlConnection(connectionString); _columnMappings = new SqlBulkCopyColumnMappingCollection(); + ColumnOrderHints = new SqlBulkCopyColumnOrderHintCollection(); _ownConnection = true; } @@ -430,6 +433,12 @@ public SqlBulkCopyColumnMappingCollection ColumnMappings } } + /// + public SqlBulkCopyColumnOrderHintCollection ColumnOrderHints + { + get; + } + /// public string DestinationTableName { @@ -692,6 +701,8 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i throw SQL.BulkLoadExistingTransaction(); } + _destColumnNames = new HashSet(); + // loop over the metadata for each column // _SqlMetaDataSet metaDataSet = internalResults[MetaDataResultId].MetaData; @@ -726,6 +737,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i } _sortedColumnMappings.Add(new _ColumnMapping(_localColumnMappings[assocId]._internalSourceColumnOrdinal, metadata)); + _destColumnNames.Add(metadata.column); nmatched++; if (nmatched > 1) @@ -872,12 +884,13 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i updateBulkCommandText.Append(")"); - if ((_copyOptions & ( + if (((_copyOptions & ( SqlBulkCopyOptions.KeepNulls | SqlBulkCopyOptions.TableLock | SqlBulkCopyOptions.CheckConstraints | SqlBulkCopyOptions.FireTriggers | SqlBulkCopyOptions.AllowEncryptedValueModifications)) != SqlBulkCopyOptions.Default) + || ColumnOrderHints.Count > 0) { bool addSeparator = false; // insert a comma character if multiple options in list ... updateBulkCommandText.Append(" with ("); @@ -906,11 +919,49 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i updateBulkCommandText.Append((addSeparator ? ", " : "") + "ALLOW_ENCRYPTED_VALUE_MODIFICATIONS"); addSeparator = true; } + if (ColumnOrderHints.Count > 0) + { + updateBulkCommandText.Append((addSeparator ? ", " : "") + TryGetOrderHintText()); + } updateBulkCommandText.Append(")"); } return (updateBulkCommandText.ToString()); } + private string TryGetOrderHintText() + { + StringBuilder orderHintText = new StringBuilder("ORDER("); + HashSet columnNames = new HashSet(); + + foreach (SqlBulkCopyColumnOrderHint columnOrderHint in ColumnOrderHints) + { + string columnNameArg = columnOrderHint.Column; + + if (!_destColumnNames.Contains(columnNameArg)) + { + // column is not valid in the destination table + throw SQL.BulkLoadOrderHintInvalidColumn(columnNameArg); + } + + if (!columnNames.Contains(columnNameArg)) + { + string columnNameEscaped = SqlServerEscapeHelper.EscapeIdentifier(SqlServerEscapeHelper.EscapeStringAsLiteral(columnNameArg)); + string sortOrderText = columnOrderHint.SortOrder == SortOrder.Descending ? "DESC" : "ASC"; + orderHintText.Append($"{columnNameEscaped} {sortOrderText}, "); + columnNames.Add(columnNameArg); + } + else + { + // duplicate column name + throw SQL.BulkLoadOrderHintDuplicateColumn(columnNameArg); + } + } + + orderHintText.Length = orderHintText.Length - 2; + orderHintText.Append(")"); + return orderHintText.ToString(); + } + // submitts the updatebulk command // private Task SubmitUpdateBulkCommand(string TDSCommand) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index 55b1141f5c..247a7d68b8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -993,6 +993,30 @@ static internal Exception BulkLoadNonMatchingColumnName(string columnName, Excep { return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadNonMatchingColumnName, columnName), e); } + internal static Exception BulkLoadNullEmptyColumnName(string paramName) + { + return ADP.Argument(string.Format(StringsHelper.GetString(Strings.SQL_ParameterCannotBeEmpty), paramName)); + } + internal static Exception BulkLoadUnspecifiedSortOrder() + { + return ADP.Argument(StringsHelper.GetString(Strings.SQL_BulkLoadUnspecifiedSortOrder)); + } + internal static Exception BulkLoadInvalidOrderHint() + { + return ADP.Argument(StringsHelper.GetString(Strings.SQL_BulkLoadInvalidOrderHint)); + } + internal static Exception BulkLoadOrderHintInaccessible() + { + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInaccessible)); + } + internal static Exception BulkLoadOrderHintInvalidColumn(string columnName) + { + return ADP.InvalidOperation(string.Format(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInvalidColumn), columnName)); + } + internal static Exception BulkLoadOrderHintDuplicateColumn(string columnName) + { + return ADP.InvalidOperation(string.Format(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintDuplicateColumn), columnName)); + } static internal Exception BulkLoadStringTooLong(string tableName, string columnName, string truncatedValue) { return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadStringTooLong, tableName, columnName, truncatedValue)); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 0e5f67f488..0c23ebc7a0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -8910,6 +8910,15 @@ internal static string SQL_BulkLoadInvalidOperationInsideEvent { } } + /// + /// Looks up a localized string similar to The given column order hint is not valid.. + /// + internal static string SQL_BulkLoadInvalidOrderHint { + get { + return ResourceManager.GetString("SQL_BulkLoadInvalidOrderHint", resourceCulture); + } + } + /// /// Looks up a localized string similar to Timeout Value '{0}' is less than 0.. /// @@ -9000,6 +9009,33 @@ internal static string SQL_BulkLoadNotAllowDBNull { } } + /// + /// Looks up a localized string similar to The column '{0}' was specified more than once.. + /// + internal static string SQL_BulkLoadOrderHintDuplicateColumn { + get { + return ResourceManager.GetString("SQL_BulkLoadOrderHintDuplicateColumn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The order hint collection is in use and cannot be accessed at this time.. + /// + internal static string SQL_BulkLoadOrderHintInaccessible { + get { + return ResourceManager.GetString("SQL_BulkLoadOrderHintInaccessible", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The sorted column '{0}' is not valid in the destination table.. + /// + internal static string SQL_BulkLoadOrderHintInvalidColumn { + get { + return ResourceManager.GetString("SQL_BulkLoadOrderHintInvalidColumn", resourceCulture); + } + } + /// /// Looks up a localized string similar to Attempt to invoke bulk copy on an object that has a pending operation.. /// @@ -9018,6 +9054,15 @@ internal static string SQL_BulkLoadStringTooLong { } } + /// + /// Looks up a localized string similar to A column order hint cannot have an unspecified sort order.. + /// + internal static string SQL_BulkLoadUnspecifiedSortOrder { + get { + return ResourceManager.GetString("SQL_BulkLoadUnspecifiedSortOrder", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed to instantiate a SqlAuthenticationInitializer with type '{0}'.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index d6f8ec6e0b..460de79689 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4530,4 +4530,19 @@ UDT size must be less than {1}, size: {0} + + The given column order hint is not valid. + + + The column '{0}' was specified more than once. + + + The order hint collection is in use and cannot be accessed at this time. + + + The sorted column '{0}' is not valid in the destination table. + + + A column order hint cannot have an unspecified sort order. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs new file mode 100644 index 0000000000..704fc780df --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Data.SqlClient +{ + /// + public sealed class SqlBulkCopyColumnOrderHint + { + /// + public SqlBulkCopyColumnOrderHint(string column, SortOrder sortOrder) + { + Column = column; + SortOrder = sortOrder; + } + + private string _columnName; + private SortOrder _sortOrder; + + /// + public string Column + { + get + { + if (_columnName != null) + { + return _columnName; + } + return string.Empty; + } + set + { + if (!string.IsNullOrEmpty(value)) + { + _columnName = value; + } + else + { + throw SQL.BulkLoadNullEmptyColumnName(nameof(Column)); + } + } + } + + /// + public SortOrder SortOrder + { + get => _sortOrder; + set + { + if (value != SortOrder.Unspecified) + { + _sortOrder = value; + } + else + { + throw SQL.BulkLoadUnspecifiedSortOrder(); + } + } + } + + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs new file mode 100644 index 0000000000..20eef779af --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; + +namespace Microsoft.Data.SqlClient +{ + /// + public sealed class SqlBulkCopyColumnOrderHintCollection : CollectionBase + { + internal bool ReadOnly { get; set; } + + /// + public SqlBulkCopyColumnOrderHint this[int index] => (SqlBulkCopyColumnOrderHint)List[index]; + + /// + public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint bulkCopyColumnOrderHint) + { + AssertWriteAccess(); + if (string.IsNullOrEmpty(bulkCopyColumnOrderHint.Column) || + bulkCopyColumnOrderHint.SortOrder == SortOrder.Unspecified) + { + throw SQL.BulkLoadInvalidOrderHint(); + } + InnerList.Add(bulkCopyColumnOrderHint); + return bulkCopyColumnOrderHint; + } + + /// + public SqlBulkCopyColumnOrderHint Add(string column, SortOrder sortOrder) + { + AssertWriteAccess(); + return Add(new SqlBulkCopyColumnOrderHint(column, sortOrder)); + } + + private void AssertWriteAccess() + { + if (ReadOnly) + { + throw SQL.BulkLoadOrderHintInaccessible(); + } + } + + /// + public new void Clear() + { + AssertWriteAccess(); + base.Clear(); + } + + /// + public bool Contains(SqlBulkCopyColumnOrderHint value) => InnerList.Contains(value); + + /// + public void CopyTo(SqlBulkCopyColumnOrderHint[] array, int index) => InnerList.CopyTo(array, index); + + /// + public int IndexOf(SqlBulkCopyColumnOrderHint value) => InnerList.IndexOf(value); + + /// + public void Insert(int index, SqlBulkCopyColumnOrderHint value) + { + AssertWriteAccess(); + InnerList.Insert(index, value); + } + + /// + public void Remove(SqlBulkCopyColumnOrderHint value) + { + AssertWriteAccess(); + InnerList.Remove(value); + } + + /// + public new void RemoveAt(int index) + { + AssertWriteAccess(); + base.RemoveAt(index); + } + + } +} + diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index f575d34900..feda69eb01 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -40,6 +40,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs new file mode 100644 index 0000000000..bbe6b99ad8 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -0,0 +1,508 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Diagnostics; + +using Xunit; + +namespace Microsoft.Data.SqlClient.Tests +{ + public class SqlBulkCopyColumnOrderHintCollectionTest + { + private static SqlBulkCopyColumnOrderHintCollection CreateCollection() => new SqlBulkCopy(new SqlConnection()).ColumnOrderHints; + + private static SqlBulkCopyColumnOrderHintCollection CreateCollection(params SqlBulkCopyColumnOrderHint[] orderHints) + { + Debug.Assert(orderHints != null); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); + + foreach (SqlBulkCopyColumnOrderHint orderHint in orderHints) + { + Debug.Assert(orderHints != null); + collection.Add(orderHint); + } + + return collection; + } + + [Fact] + public void Properties_ReturnFalse() + { + IList list = CreateCollection(); + Assert.False(list.IsSynchronized); + Assert.False(list.IsFixedSize); + Assert.False(list.IsReadOnly); + } + + [Fact] + public void Methods_NullParameterPassed_ThrowsArgumentNullException() + { + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); + collection.Add(new SqlBulkCopyColumnOrderHint("column", SortOrder.Ascending)); + + Assert.Throws(() => collection.CopyTo(null, 0)); + + // Passing null to the public Add method should really throw ArgumentNullException + // (which would be consistent with the explicit implementation of IList.Add), but + // the full framework does not check for null in the public Add method. Instead it + // accesses the parameter without first checking for null, resulting in + // NullReferenceExpcetion being thrown. + Assert.Throws(() => collection.Add(null)); + + // Passing null to the public Insert and Remove methods should really throw + // ArgumentNullException (which would be consistent with the explicit + // implementations of IList.Insert and IList.Remove), but the full framework + // does not check for null in these methods. + collection.Insert(0, null); + collection.Remove(null); + + IList list = collection; + Assert.Throws(() => list[0] = null); + Assert.Throws(() => list.Add(null)); + Assert.Throws(() => list.CopyTo(null, 0)); + Assert.Throws(() => list.Insert(0, null)); + Assert.Throws(() => list.Remove(null)); + } + + [Fact] + public void Members_InvalidRange_ThrowsArgumentOutOfRangeException() + { + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); + + var item = new SqlBulkCopyColumnOrderHint("column", SortOrder.Ascending); + + Assert.Throws(() => collection[-1]); + Assert.Throws(() => collection[collection.Count]); + Assert.Throws(() => collection.Insert(-1, item)); + Assert.Throws(() => collection.Insert(collection.Count + 1, item)); + Assert.Throws(() => collection.RemoveAt(-1)); + Assert.Throws(() => collection.RemoveAt(collection.Count)); + + IList list = collection; + Assert.Throws(() => list[-1]); + Assert.Throws(() => list[collection.Count]); + Assert.Throws(() => list[-1] = item); + Assert.Throws(() => list[collection.Count] = item); + Assert.Throws(() => list.Insert(-1, item)); + Assert.Throws(() => list.Insert(collection.Count + 1, item)); + Assert.Throws(() => list.RemoveAt(-1)); + Assert.Throws(() => list.RemoveAt(collection.Count)); + } + + [Fact] + public void Add_AddItems_ItemsAddedAsEpected() + { + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + Assert.Same(item1, collection.Add(item1)); + Assert.Same(item1, collection[0]); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + Assert.Same(item2, collection.Add(item2)); + Assert.Same(item2, collection[1]); + + IList list = CreateCollection(); + int index = list.Add(item1); + Assert.Equal(0, index); + Assert.Same(item1, list[0]); + index = list.Add(item2); + Assert.Equal(1, index); + Assert.Same(item2, list[1]); + } + + [Fact] + public void Add_HelperOverloads_ItemsAddedAsExpected() + { + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); + SqlBulkCopyColumnOrderHint item; + + item = collection.Add("column1", SortOrder.Ascending); + Assert.NotNull(item); + Assert.Equal("column1", item.Column); + Assert.Equal(SortOrder.Ascending, item.SortOrder); + } + + [Fact] + public void Add_InvalidItems_ThrowsInvalidOperationException() + { + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); + Assert.Throws(() => collection.Add(new SqlBulkCopyColumnOrderHint(null, SortOrder.Ascending))); + Assert.Throws(() => collection.Add(new SqlBulkCopyColumnOrderHint(null, SortOrder.Unspecified))); + Assert.Throws(() => collection.Add(new SqlBulkCopyColumnOrderHint("", SortOrder.Descending))); + Assert.Throws(() => collection.Add(new SqlBulkCopyColumnOrderHint("", SortOrder.Unspecified))); + Assert.Throws(() => collection.Add(new SqlBulkCopyColumnOrderHint("column", SortOrder.Unspecified))); + + IList list = CreateCollection(); + Assert.Throws(() => list.Add(new SqlBulkCopyColumnOrderHint(null, SortOrder.Ascending))); + Assert.Throws(() => list.Add(new SqlBulkCopyColumnOrderHint(null, SortOrder.Unspecified))); + Assert.Throws(() => list.Add(new SqlBulkCopyColumnOrderHint("", SortOrder.Descending))); + Assert.Throws(() => list.Add(new SqlBulkCopyColumnOrderHint("", SortOrder.Unspecified))); + Assert.Throws(() => list.Add(new SqlBulkCopyColumnOrderHint("column", SortOrder.Unspecified))); + } + + [Fact] + public void IListAddInsert_InsertNonSqlBulkCopyColumnOrderHintItems_DoNotThrow() + { + IList list = CreateCollection(); + list.Add(new SqlBulkCopyColumnOrderHint("column", SortOrder.Descending)); + + // The following operations should really throw ArgumentException due to the + // mismatched types, but do not throw in the full framework. + string bogus = "Bogus"; + list[0] = bogus; + list.Add(bogus); + list.Insert(0, bogus); + } + + [Fact] + public void GetEnumerator_NoItems_EmptyEnumerator() + { + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); + IEnumerator e = collection.GetEnumerator(); + Assert.Throws(() => e.Current); + Assert.False(e.MoveNext()); + Assert.Throws(() => e.Current); + } + + [Fact] + public void GetEnumerator_ItemsAdded_AllItemsReturnedAndEnumeratorBehavesAsExpected() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Descending); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(item1, item2, item3); + + IEnumerator e = collection.GetEnumerator(); + + const int Iterations = 2; + for (int i = 0; i < Iterations; i++) + { + // Not started + Assert.Throws(() => e.Current); + + Assert.True(e.MoveNext()); + Assert.Same(item1, e.Current); + + Assert.True(e.MoveNext()); + Assert.Same(item2, e.Current); + + Assert.True(e.MoveNext()); + Assert.Same(item3, e.Current); + + Assert.False(e.MoveNext()); + Assert.False(e.MoveNext()); + Assert.False(e.MoveNext()); + Assert.False(e.MoveNext()); + Assert.False(e.MoveNext()); + + // Ended + Assert.Throws(() => e.Current); + + e.Reset(); + } + } + + [Fact] + public void GetEnumerator_ItemsAdded_ItemsFromEnumeratorMatchesItemsFromIndexer() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Descending); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(item1, item2, item3); + int index = 0; + foreach (SqlBulkCopyColumnOrderHint enumeratorItem in collection) + { + SqlBulkCopyColumnOrderHint indexerItem = collection[index]; + + Assert.NotNull(enumeratorItem); + Assert.NotNull(indexerItem); + + Assert.Same(indexerItem, enumeratorItem); + index++; + } + } + + [Fact] + public void GetEnumerator_ModifiedCollectionDuringEnumeration_ThrowsInvalidOperationException() + { + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); + + IEnumerator e = collection.GetEnumerator(); + + collection.Add("column", SortOrder.Ascending); + + // Collection changed. + Assert.Throws(() => e.MoveNext()); + Assert.Throws(() => e.Reset()); + } + + [Fact] + public void Contains_ItemsAdded_MatchesExpectation() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(item1, item2, item3); + + Assert.True(collection.Contains(item1)); + Assert.True(collection.Contains(item2)); + Assert.True(collection.Contains(item3)); + Assert.False(collection.Contains(null)); + + IList list = collection; + Assert.True(list.Contains(item1)); + Assert.True(list.Contains(item2)); + Assert.True(list.Contains(item3)); + Assert.False(list.Contains(null)); + Assert.False(list.Contains("Bogus")); + } + + [Fact] + public void CopyTo_ItemsAdded_ItemsCopiedToArray() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(item1, item2, item3); + + var array1 = new SqlBulkCopyColumnOrderHint[collection.Count]; + collection.CopyTo(array1, 0); + + Assert.Same(item1, array1[0]); + Assert.Same(item2, array1[1]); + Assert.Same(item3, array1[2]); + + var array2 = new SqlBulkCopyColumnOrderHint[collection.Count]; + ((ICollection)collection).CopyTo(array2, 0); + + Assert.Same(item1, array2[0]); + Assert.Same(item2, array2[1]); + Assert.Same(item3, array2[2]); + } + + [Fact] + public void CopyTo_InvalidArrayType_Throws() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + + ICollection collection = CreateCollection(item1, item2, item3); + + Assert.Throws(() => collection.CopyTo(new int[collection.Count], 0)); + Assert.Throws(() => collection.CopyTo(new string[collection.Count], 0)); + } + + [Fact] + public void Indexer_BehavesAsExpected() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(item1, item2, item3); + + Assert.Same(item1, collection[0]); + Assert.Same(item2, collection[1]); + Assert.Same(item3, collection[2]); + + IList list = collection; + list[0] = item2; + list[1] = item3; + list[2] = item1; + Assert.Same(item2, list[0]); + Assert.Same(item3, list[1]); + Assert.Same(item1, list[2]); + } + + [Fact] + public void IndexOf_BehavesAsExpected() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(item1, item2); + + Assert.Equal(0, collection.IndexOf(item1)); + Assert.Equal(1, collection.IndexOf(item2)); + Assert.Equal(-1, collection.IndexOf(item3)); + + IList list = collection; + Assert.Equal(0, list.IndexOf(item1)); + Assert.Equal(1, list.IndexOf(item2)); + Assert.Equal(-1, list.IndexOf(item3)); + Assert.Equal(-1, list.IndexOf("bogus")); + } + + [Fact] + public void Insert_BehavesAsExpected() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); + + collection.Insert(0, item3); + collection.Insert(0, item2); + collection.Insert(0, item1); + + Assert.Equal(3, collection.Count); + Assert.Same(item1, collection[0]); + Assert.Same(item2, collection[1]); + Assert.Same(item3, collection[2]); + } + + [Fact] + public void InsertAndClear_BehavesAsExpected() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); + + collection.Insert(0, item1); + collection.Insert(1, item2); + collection.Insert(2, item3); + Assert.Equal(3, collection.Count); + Assert.Same(item1, collection[0]); + Assert.Same(item2, collection[1]); + Assert.Same(item3, collection[2]); + + collection.Clear(); + Assert.Empty(collection); + + collection.Add(item1); + collection.Add(item3); + Assert.Equal(2, collection.Count); + Assert.Same(item1, collection[0]); + Assert.Same(item3, collection[1]); + + collection.Insert(1, item2); + Assert.Equal(3, collection.Count); + Assert.Same(item1, collection[0]); + Assert.Same(item2, collection[1]); + Assert.Same(item3, collection[2]); + + collection.Clear(); + Assert.Empty(collection); + + IList list = collection; + list.Insert(0, item1); + list.Insert(1, item2); + list.Insert(2, item3); + Assert.Equal(3, list.Count); + Assert.Same(item1, list[0]); + Assert.Same(item2, list[1]); + Assert.Same(item3, list[2]); + + list.Clear(); + Assert.Equal(0, list.Count); + + list.Add(item1); + list.Add(item3); + Assert.Equal(2, list.Count); + Assert.Same(item1, list[0]); + Assert.Same(item3, list[1]); + + list.Insert(1, item2); + Assert.Equal(3, list.Count); + Assert.Same(item1, list[0]); + Assert.Same(item2, list[1]); + Assert.Same(item3, list[2]); + + list.Clear(); + Assert.Equal(0, list.Count); + } + + [Fact] + public void Remove_BehavesAsExpected() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(item1, item2); + + collection.Remove(item1); + Assert.Single(collection); + Assert.Same(item2, collection[0]); + + collection.Remove(item2); + Assert.Empty(collection); + + // The explicit implementation of IList.Remove throws ArgumentException if + // the item isn't in the collection, but the public Remove method does not + // throw in the full framework. + collection.Remove(item2); + collection.Remove(new SqlBulkCopyColumnOrderHint("column3", SortOrder.Descending)); + + + IList list = CreateCollection(item1, item2); + + list.Remove(item1); + Assert.Equal(1, list.Count); + Assert.Same(item2, list[0]); + + list.Remove(item2); + Assert.Equal(0, list.Count); + + AssertExtensions.Throws(null, () => list.Remove(item2)); + AssertExtensions.Throws(null, () => list.Remove(new SqlBulkCopyColumnOrderHint("column4", SortOrder.Ascending))); + AssertExtensions.Throws(null, () => list.Remove("bogus")); + } + + [Fact] + public void RemoveAt_BehavesAsExpected() + { + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); + var item2 = new SqlBulkCopyColumnOrderHint("column2", SortOrder.Descending); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + + SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(item1, item2, item3); + + collection.RemoveAt(0); + Assert.Equal(2, collection.Count); + Assert.Same(item2, collection[0]); + Assert.Same(item3, collection[1]); + + collection.RemoveAt(1); + Assert.Single(collection); + Assert.Same(item2, collection[0]); + + collection.RemoveAt(0); + Assert.Empty(collection); + + + IList list = CreateCollection(item1, item2, item3); + + list.RemoveAt(0); + Assert.Equal(2, list.Count); + Assert.Same(item2, list[0]); + Assert.Same(item3, list[1]); + list.RemoveAt(1); + Assert.Equal(1, list.Count); + Assert.Same(item2, list[0]); + + list.RemoveAt(0); + Assert.Equal(0, list.Count); + } + + [Fact] + public void SyncRoot_NotNullAndSameObject() + { + ICollection collection = CreateCollection(); + Assert.NotNull(collection.SyncRoot); + Assert.Same(collection.SyncRoot, collection.SyncRoot); + } + + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index 7db44f4358..d73b870b42 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -59,6 +59,12 @@ + + + + + + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs new file mode 100644 index 0000000000..d58b1b38a1 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs @@ -0,0 +1,101 @@ +using System; +using System.Data.Common; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + public class OrderHint + { + private static readonly string destinationTable = null; + private static readonly string sourceTable = "Customers"; + private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; + private static readonly string sourceQueryTemplate = "SELECT CustomerId, CompanyName, ContactName FROM {0}"; + private static readonly string getRowCountQueryTemplate = "SELECT COUNT(*) FROM {0}"; + + public static void Test(string srcConstr, string dstConstr, string dstTable) + { + dstTable = destinationTable != null ? destinationTable : dstTable; + string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); + string initialQuery = string.Format(initialQueryTemplate, dstTable); + string getRowCountQuery = string.Format(getRowCountQueryTemplate, sourceTable); + + using (SqlConnection dstConn = new SqlConnection(dstConstr)) + using (SqlCommand dstCmd = dstConn.CreateCommand()) + { + dstConn.Open(); + Helpers.TryExecute(dstCmd, initialQuery); + using (SqlConnection srcConn = new SqlConnection(srcConstr)) + using (SqlCommand srcCmd = new SqlCommand(getRowCountQuery, srcConn)) + { + srcConn.Open(); + try + { + int nRowsInSource = Convert.ToInt32(srcCmd.ExecuteScalar()); + srcCmd.CommandText = sourceQuery; + using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn)) + { + bulkcopy.DestinationTableName = dstTable; + + // no hints + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + bulkcopy.WriteToServer(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource); + } + + // hint for 1 of 3 columns + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + bulkcopy.ColumnOrderHints.Add("CustomerID", SortOrder.Ascending); + bulkcopy.WriteToServer(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 2); + } + + // hints for all 3 columns + // order of hints is not the same as column order in table + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + bulkcopy.ColumnOrderHints.Add("ContactName", SortOrder.Descending); + bulkcopy.ColumnOrderHints.Add("CompanyName", SortOrder.Ascending); + bulkcopy.WriteToServer(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 3); + } + + // add column mappings + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + bulkcopy.ColumnMappings.Add(0, 1); + bulkcopy.ColumnMappings.Add(1, 2); + bulkcopy.ColumnMappings.Add(2, 0); + bulkcopy.WriteToServer(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 4); + } + } + + // add copy options + SqlBulkCopyOptions copyOptions = + SqlBulkCopyOptions.AllowEncryptedValueModifications + | SqlBulkCopyOptions.FireTriggers + | SqlBulkCopyOptions.KeepNulls; + using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn, copyOptions, null)) + { + bulkcopy.DestinationTableName = dstTable; + bulkcopy.ColumnOrderHints.Add("CustomerID", SortOrder.Ascending); + bulkcopy.ColumnOrderHints.Add("CompanyName", SortOrder.Ascending); + bulkcopy.ColumnOrderHints.Add("ContactName", SortOrder.Descending); + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + bulkcopy.WriteToServer(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 5); + } + } + } + finally + { + Helpers.TryExecute(dstCmd, "drop table " + dstTable); + } + } + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs new file mode 100644 index 0000000000..773780da42 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs @@ -0,0 +1,108 @@ +using System; +using System.Data.Common; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + public class OrderHintAsync + { + private static readonly string destinationTable = null; + private static readonly string sourceTable = "Customers"; + private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; + private static readonly string sourceQueryTemplate = "SELECT CustomerId, CompanyName, ContactName FROM {0}"; + private static readonly string getRowCountQueryTemplate = "SELECT COUNT(*) FROM {0}"; + + public static void Test(string srcConstr, string dstConstr, string dstTable) + { + Task t = TestAsync(srcConstr, dstConstr, dstTable); + t.Wait(); + Assert.True(t.IsCompleted, "Task did not complete! Status: " + t.Status); + } + + public static async Task TestAsync(string srcConstr, string dstConstr, string dstTable) + { + dstTable = destinationTable != null ? destinationTable : dstTable; + string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); + string initialQuery = string.Format(initialQueryTemplate, dstTable); + string getRowCountQuery = string.Format(getRowCountQueryTemplate, sourceTable); + + using (SqlConnection dstConn = new SqlConnection(dstConstr)) + using (SqlCommand dstCmd = dstConn.CreateCommand()) + { + await dstConn.OpenAsync(); + Helpers.TryExecute(dstCmd, initialQuery); + using (SqlConnection srcConn = new SqlConnection(srcConstr)) + using (SqlCommand srcCmd = new SqlCommand(getRowCountQuery, srcConn)) + { + await srcConn.OpenAsync(); + try + { + int nRowsInSource = Convert.ToInt32(await srcCmd.ExecuteScalarAsync()); + srcCmd.CommandText = sourceQuery; + using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn)) + { + bulkcopy.DestinationTableName = dstTable; + + // no hints + using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) + { + await bulkcopy.WriteToServerAsync(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource); + } + + // hint for 1 of 3 columns + using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) + { + bulkcopy.ColumnOrderHints.Add("CustomerID", SortOrder.Ascending); + await bulkcopy.WriteToServerAsync(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 2); + } + + // hints for all 3 columns + // order of hints is not the same as column order in table + using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) + { + bulkcopy.ColumnOrderHints.Add("ContactName", SortOrder.Descending); + bulkcopy.ColumnOrderHints.Add("CompanyName", SortOrder.Ascending); + await bulkcopy.WriteToServerAsync(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 3); + } + + // add column mappings + using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) + { + bulkcopy.ColumnMappings.Add(0, 1); + bulkcopy.ColumnMappings.Add(1, 2); + bulkcopy.ColumnMappings.Add(2, 0); + await bulkcopy.WriteToServerAsync(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 4); + } + } + + // add copy options + SqlBulkCopyOptions copyOptions = SqlBulkCopyOptions.AllowEncryptedValueModifications + | SqlBulkCopyOptions.FireTriggers + | SqlBulkCopyOptions.KeepNulls; + using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn, copyOptions, null)) + { + bulkcopy.DestinationTableName = dstTable; + bulkcopy.ColumnOrderHints.Add("CustomerID", SortOrder.Ascending); + bulkcopy.ColumnOrderHints.Add("CompanyName", SortOrder.Ascending); + bulkcopy.ColumnOrderHints.Add("ContactName", SortOrder.Descending); + using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) + { + await bulkcopy.WriteToServerAsync(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 5); + } + } + } + finally + { + Helpers.TryExecute(dstCmd, "drop table " + dstTable); + } + } + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs new file mode 100644 index 0000000000..0fd97d90b2 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs @@ -0,0 +1,56 @@ +using System; +using System.Data.Common; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + public class OrderHintDuplicateColumn + { + private static readonly string destinationTable = null; + private static readonly string sourceTable = "Customers"; + private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; + private static readonly string sourceQueryTemplate = "SELECT CustomerID, CompanyName, ContactName FROM {0}"; + + public static void Test(string srcConstr, string dstConstr, string dstTable) + { + dstTable = destinationTable != null ? destinationTable : dstTable; + string initialQuery = string.Format(initialQueryTemplate, dstTable); + string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); + + using (SqlConnection dstConn = new SqlConnection(dstConstr)) + using (SqlCommand dstCmd = dstConn.CreateCommand()) + { + dstConn.Open(); + Helpers.TryExecute(dstCmd, initialQuery); + using (SqlConnection srcConn = new SqlConnection(srcConstr)) + using (SqlCommand srcCmd = new SqlCommand(sourceQuery, srcConn)) + { + srcConn.Open(); + + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn)) + { + try + { + bulkcopy.DestinationTableName = dstTable; + string destColumn = "CompanyName"; + bulkcopy.ColumnOrderHints.Add(destColumn, SortOrder.Ascending); + bulkcopy.ColumnOrderHints.Add(destColumn, SortOrder.Descending); + + string expectedErrorMsg = string.Format( + SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintDuplicateColumn, destColumn); + DataTestUtility.AssertThrowsWrapper( + () => bulkcopy.WriteToServer(reader), + exceptionMessage: expectedErrorMsg); + } + finally + { + Helpers.TryExecute(dstCmd, "drop table " + dstTable); + } + } + } + } + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs new file mode 100644 index 0000000000..4fe3b74820 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs @@ -0,0 +1,64 @@ +using System; +using System.Data.Common; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + public class OrderHintIdentityColumn + { + private static readonly string destinationTable = null; + private static readonly string sourceTable = "Customers"; + private static readonly string initialQueryTemplate = "create table {0} (CustomerID int IDENTITY NOT NULL, CompanyName nvarchar(50), ContactName nvarchar(50))"; + private static readonly string sourceQueryTemplate = "SELECT CustomerId, CompanyName, ContactName FROM {0}"; + private static readonly string getRowCountQueryTemplate = "SELECT COUNT(*) FROM {0}"; + + public static void Test(string srcConstr, string dstConstr, string dstTable) + { + dstTable = destinationTable != null ? destinationTable : dstTable; + string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); + string initialQuery = string.Format(initialQueryTemplate, dstTable); + string getRowCountQuery = string.Format(getRowCountQueryTemplate, sourceTable); + + using (SqlConnection dstConn = new SqlConnection(dstConstr)) + using (SqlCommand dstCmd = dstConn.CreateCommand()) + { + dstConn.Open(); + Helpers.TryExecute(dstCmd, initialQuery); + using (SqlConnection srcConn = new SqlConnection(srcConstr)) + using (SqlCommand srcCmd = new SqlCommand(getRowCountQuery, srcConn)) + { + srcConn.Open(); + try + { + int nRowsInSource = Convert.ToInt32(srcCmd.ExecuteScalar()); + srcCmd.CommandText = sourceQuery; + using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn)) + { + bulkcopy.DestinationTableName = dstTable; + + // no mapping + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + bulkcopy.ColumnOrderHints.Add("CustomerID", SortOrder.Ascending); + bulkcopy.WriteToServer(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource); + } + + // with mapping + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + bulkcopy.ColumnMappings.Add(0, 1); + bulkcopy.WriteToServer(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource); + } + } + } + finally + { + Helpers.TryExecute(dstCmd, "drop table " + dstTable); + } + } + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs new file mode 100644 index 0000000000..b54bff039d --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs @@ -0,0 +1,72 @@ +using System; +using System.Data.Common; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + public class OrderHintMissingTargetColumn + { + private static readonly string destinationTable = null; + private static readonly string sourceTable = "Customers"; + private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; + private static readonly string sourceQueryTemplate = "SELECT CustomerID, CompanyName, ContactName FROM {0}"; + + public static void Test(string srcConstr, string dstConstr, string dstTable) + { + dstTable = destinationTable != null ? destinationTable : dstTable; + string initialQuery = string.Format(initialQueryTemplate, dstTable); + string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); + + using (SqlConnection dstConn = new SqlConnection(dstConstr)) + using (SqlCommand dstCmd = dstConn.CreateCommand()) + { + dstConn.Open(); + Helpers.TryExecute(dstCmd, initialQuery); + using (SqlConnection srcConn = new SqlConnection(srcConstr)) + using (SqlCommand srcCmd = new SqlCommand(sourceQuery, srcConn)) + { + srcConn.Open(); + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn)) + { + try + { + bulkcopy.DestinationTableName = dstTable; + string nonexistentColumn = "nonexistent column"; + string sourceColumn = "CustomerID"; + string destColumn = "ContactName"; + + + // column does not exist in destination table + bulkcopy.ColumnOrderHints.Add(nonexistentColumn, SortOrder.Ascending); + + string expectedErrorMsg = string.Format( + SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintInvalidColumn, nonexistentColumn); + DataTestUtility.AssertThrowsWrapper( + () => bulkcopy.WriteToServer(reader), + exceptionMessage: expectedErrorMsg); + + // column does not exist in destination table because of user-defined mapping + bulkcopy.ColumnOrderHints.RemoveAt(0); + bulkcopy.ColumnOrderHints.Add(sourceColumn, SortOrder.Ascending); + Assert.True(bulkcopy.ColumnOrderHints.Count == 1, "Error adding a column order hint"); + + expectedErrorMsg = string.Format( + SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintInvalidColumn, sourceColumn); + bulkcopy.ColumnMappings.Add(sourceColumn, destColumn); + DataTestUtility.AssertThrowsWrapper( + () => bulkcopy.WriteToServer(reader), + exceptionMessage: expectedErrorMsg); + } + finally + { + Helpers.TryExecute(dstCmd, "drop table " + dstTable); + } + } + } + } + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs new file mode 100644 index 0000000000..e2f26dc142 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs @@ -0,0 +1,52 @@ +using System.Data.Common; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + class OrderHintTransaction + { + private static readonly string destinationTable = null; + private static readonly string sourceTable = "Customers"; + private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; + private static readonly string sourceQueryTemplate = "SELECT CustomerID, CompanyName, ContactName FROM {0}"; + + public static void Test(string srcConstr, string dstConstr, string dstTable) + { + dstTable = destinationTable != null ? destinationTable : dstTable; + string initialQuery = string.Format(initialQueryTemplate, dstTable); + string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); + + using (SqlConnection dstConn = new SqlConnection(dstConstr)) + using (SqlCommand dstCmd = dstConn.CreateCommand()) + { + dstConn.Open(); + Helpers.TryExecute(dstCmd, initialQuery); + using (SqlConnection srcConn = new SqlConnection(srcConstr)) + using (SqlCommand srcCmd = new SqlCommand(sourceQuery, srcConn)) + { + srcConn.Open(); + SqlTransaction txn = dstConn.BeginTransaction(); + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + using (SqlBulkCopy bulkcopy = new SqlBulkCopy( + dstConn, SqlBulkCopyOptions.CheckConstraints, txn)) + { + try + { + bulkcopy.DestinationTableName = dstTable; + bulkcopy.ColumnMappings.Add(0, 2); + bulkcopy.ColumnMappings.Add(2, 0); + bulkcopy.ColumnOrderHints.Add("CustomerID", SortOrder.Ascending); + bulkcopy.ColumnOrderHints.Add("ContactName", SortOrder.Descending); + bulkcopy.WriteToServer(reader); + } + finally + { + txn.Rollback(); + } + } + } + } + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs index 4219838302..6b05044354 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs @@ -247,5 +247,43 @@ public void DestinationTableNameWithSpecialCharTest() { DestinationTableNameWithSpecialChar.Test(srcConstr, AddGuid("SqlBulkCopyTest_DestinationTableNameWithSpecialChar")); } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void OrderHintTest() + { + OrderHint.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHint")); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void OrderHintMissingTargetColumnTest() + { + OrderHintMissingTargetColumn.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintMissingTargetColumn")); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void OrderHintDuplicateColumnTest() + { + OrderHintDuplicateColumn.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintDuplicateColumn")); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void OrderHintTransactionTest() + { + OrderHintTransaction.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintTransaction")); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void OrderHintAsyncTest() + { + OrderHintAsync.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintAsync")); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ActiveIssue(1)] + public void OrderHintIdentityColumnTest() + { + OrderHintIdentityColumn.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintIdentityColumn")); + } + } } From 84ec1ab1205e67dc80465747a4494d3eb678404b Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Fri, 24 Apr 2020 13:00:35 -0700 Subject: [PATCH 02/34] minor changes --- .../tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs | 6 +++++- .../tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs | 6 +++++- .../SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs | 6 +++++- .../SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs | 6 +++++- .../SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs | 6 +++++- .../ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs | 6 +++++- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs index d58b1b38a1..447d964727 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Data.Common; using Xunit; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs index 773780da42..cee0f03c49 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Data.Common; using System.Threading.Tasks; using Xunit; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs index 0fd97d90b2..b0c247ce95 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Data.Common; namespace Microsoft.Data.SqlClient.ManualTesting.Tests diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs index 4fe3b74820..b0fd10e3f7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Data.Common; using Xunit; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs index b54bff039d..10bc0ec95b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Data.Common; using Xunit; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs index e2f26dc142..cc5b743844 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs @@ -1,4 +1,8 @@ -using System.Data.Common; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Data.Common; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { From bef48f6722321455c23a662141544ad56339b807 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Mon, 27 Apr 2020 12:06:26 -0700 Subject: [PATCH 03/34] minor changes --- doc/samples/SqlBulkCopy_ColumnOrderHint.cs | 3 ++- .../SqlBulkCopy_ColumnOrderHintColumn.cs | 3 ++- .../SqlBulkCopy_ColumnOrderHintSortOrder.cs | 3 ++- .../SqlBulkCopyColumnOrderHint.xml | 1 - src/Microsoft.Data.SqlClient.sln | 16 ++++++------ .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 2 ++ .../netcore/src/Resources/SR.resx | 2 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 26 ++++++------------- .../netfx/src/Resources/Strings.resx | 2 +- ...qlBulkCopyColumnOrderHintCollectionTest.cs | 3 --- .../OrderHintMissingTargetColumn.cs | 1 - 11 files changed, 26 insertions(+), 36 deletions(-) diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHint.cs b/doc/samples/SqlBulkCopy_ColumnOrderHint.cs index fd3e003b3a..9960debf20 100644 --- a/doc/samples/SqlBulkCopy_ColumnOrderHint.cs +++ b/doc/samples/SqlBulkCopy_ColumnOrderHint.cs @@ -80,4 +80,5 @@ private static string GetConnectionString() " Integrated Security=true;" + "Initial Catalog=AdventureWorks;"; } -} \ No newline at end of file +} +// diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintColumn.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintColumn.cs index 963d5496d2..6a75e0289c 100644 --- a/doc/samples/SqlBulkCopy_ColumnOrderHintColumn.cs +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintColumn.cs @@ -81,4 +81,5 @@ private static string GetConnectionString() " Integrated Security=true;" + "Initial Catalog=AdventureWorks;"; } -} \ No newline at end of file +} +// diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintSortOrder.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintSortOrder.cs index c83f048012..54cef5da53 100644 --- a/doc/samples/SqlBulkCopy_ColumnOrderHintSortOrder.cs +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintSortOrder.cs @@ -81,4 +81,5 @@ private static string GetConnectionString() " Integrated Security=true;" + "Initial Catalog=AdventureWorks;"; } -} \ No newline at end of file +} +// diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml index 6e376d698b..f9f43c1e0f 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml @@ -41,7 +41,6 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. - The name of the destination column within the destination table. diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 0da3b93aa9..7aae5eafa9 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -767,16 +767,16 @@ Global {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp2.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp2.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp2.1-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp2.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.Build.0 = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x64.ActiveCfg = Release|Any CPU diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index f92a6eaa07..ab9d511134 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -603,6 +603,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i int nmatched = 0; // Number of columns that match and are accepted int nrejected = 0; // Number of columns that match but were rejected bool rejectColumn; // True if a column is rejected because of an excluded type + bool isInTransaction; isInTransaction = _connection.HasLocalTransaction; @@ -1345,6 +1346,7 @@ private void CreateOrValidateConnection(string method) { throw ADP.ConnectionRequired(method); } + if (_ownConnection && _connection.State != ConnectionState.Open) { _connection.Open(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index 95559ff614..992c60ae79 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1878,4 +1878,4 @@ The sorted column '{0}' is not valid in the destination table. - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index d8958b4d68..f54c9ba5ea 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -246,16 +246,10 @@ - - Component - - - Component - + + - - Component - + @@ -263,9 +257,7 @@ - - Component - + @@ -337,9 +329,7 @@ - - Component - + @@ -393,10 +383,10 @@ - + True True - Strings.resx + $(ResxFileName).resx @@ -452,4 +442,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 460de79689..96b03a0cb0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4545,4 +4545,4 @@ A column order hint cannot have an unspecified sort order. - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index bbe6b99ad8..bfbc81f0cc 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -445,7 +445,6 @@ public void Remove_BehavesAsExpected() collection.Remove(item2); collection.Remove(new SqlBulkCopyColumnOrderHint("column3", SortOrder.Descending)); - IList list = CreateCollection(item1, item2); list.Remove(item1); @@ -481,7 +480,6 @@ public void RemoveAt_BehavesAsExpected() collection.RemoveAt(0); Assert.Empty(collection); - IList list = CreateCollection(item1, item2, item3); list.RemoveAt(0); @@ -503,6 +501,5 @@ public void SyncRoot_NotNullAndSameObject() Assert.NotNull(collection.SyncRoot); Assert.Same(collection.SyncRoot, collection.SyncRoot); } - } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs index 10bc0ec95b..cd9a8d324f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs @@ -41,7 +41,6 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) string sourceColumn = "CustomerID"; string destColumn = "ContactName"; - // column does not exist in destination table bulkcopy.ColumnOrderHints.Add(nonexistentColumn, SortOrder.Ascending); From 51b37d84f549c82099c78333c71b29f365f48997 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Mon, 27 Apr 2020 12:09:01 -0700 Subject: [PATCH 04/34] Update Microsoft.Data.SqlClient.sln --- src/Microsoft.Data.SqlClient.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 7aae5eafa9..f7f91832e5 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -767,7 +767,7 @@ Global {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 From eb821410fb122096fa6649b33fa74899f6c2df8f Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Mon, 27 Apr 2020 13:40:39 -0700 Subject: [PATCH 05/34] modified tests --- src/Microsoft.Data.SqlClient.sln | 16 +++++++-------- .../SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs | 20 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 7aae5eafa9..0da3b93aa9 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -767,16 +767,16 @@ Global {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp2.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp2.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp2.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp2.1-Debug|x86 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp2.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|Any CPU.Build.0 = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.Release|x64.ActiveCfg = Release|Any CPU diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs index 6b05044354..fd9d86598d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs @@ -248,37 +248,37 @@ public void DestinationTableNameWithSpecialCharTest() DestinationTableNameWithSpecialChar.Test(srcConstr, AddGuid("SqlBulkCopyTest_DestinationTableNameWithSpecialChar")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] public void OrderHintTest() { OrderHint.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHint")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] public void OrderHintMissingTargetColumnTest() { OrderHintMissingTargetColumn.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintMissingTargetColumn")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] public void OrderHintDuplicateColumnTest() { OrderHintDuplicateColumn.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintDuplicateColumn")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] public void OrderHintTransactionTest() { OrderHintTransaction.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintTransaction")); - } - - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] public void OrderHintAsyncTest() { OrderHintAsync.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintAsync")); - } - - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] [ActiveIssue(1)] public void OrderHintIdentityColumnTest() { From b25e3c87971af9513396d96a63491a9afbd0611a Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Tue, 28 Apr 2020 16:22:45 -0700 Subject: [PATCH 06/34] Tests now use DataTestUtility to drop tables. Added tests for WriteToServer overloads. Fixed XML mistakes --- .../SqlBulkCopyColumnOrderHint.xml | 7 +- .../SqlBulkCopyColumnOrderHintCollection.xml | 28 ++++---- .../SQL/SqlBulkCopyTest/OrderHint.cs | 71 ++++++++++++------- .../SQL/SqlBulkCopyTest/OrderHintAsync.cs | 2 +- .../OrderHintDuplicateColumn.cs | 2 +- .../OrderHintIdentityColumn.cs | 2 +- .../OrderHintMissingTargetColumn.cs | 2 +- .../SqlBulkCopyTest/OrderHintTransaction.cs | 4 +- 8 files changed, 71 insertions(+), 47 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml index 6e376d698b..586b5d5f93 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml @@ -22,10 +22,10 @@ unordered if no hints are provided. The column names supplied must be valid column names in the destination table. The order in which hints can be specified is arbitrary. A single column name cannot be specified more than once. -If the collection is not empty, order hints can only be provided for valid +If the collection is not empty, order hints can only be provided for valid destination columns which have been mapped. -If a of Unspecified is given, an will be thrown. +If a of Unspecified is given, an will be thrown. ## Examples The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. @@ -41,7 +41,6 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. - The name of the destination column within the destination table. @@ -114,7 +113,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. will be thrown if a of Unspecified is given. +An will be thrown if a of Unspecified is given. The last value set takes precedence. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index 04a95c0281..796c98cd31 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -18,10 +18,10 @@ unordered if no hints are provided. The column names supplied must be valid column names in the destination table. The order in which hints can be specified is arbitrary. A single column name cannot be specified more than once. -If the collection is not empty, order hints can only be provided for valid +If the collection is not empty, order hints can only be provided for valid destination columns which have been mapped. -If a of Unspecified is given, an will be thrown. +If a of Unspecified is given, an will be thrown. ## Examples @@ -75,7 +75,7 @@ it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to c ## Examples The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. -A SqlBulkCopyColumnOrderHint object is added to the by providing the destination column name and its sort order. +A SqlBulkCopyColumnOrderHint object is added to the by providing the destination column name and its sort order. > [!IMPORTANT] > This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). @@ -94,9 +94,9 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. method is most commonly used when you use a single +The method is most commonly used when you use a single instance to process more than one bulk copy operation. If you create column order hints for one bulk copy operation, you must clear the - after the method and before processing the next bulk copy. + after the method and before processing the next bulk copy. Performing several bulk copies using the same instance will usually be more efficient from a performance point of view than using a separate for each operation. @@ -104,7 +104,7 @@ Performing several bulk copies using the same method must be used after the first bulk copy is performed and before the next bulk copy's order hint is defined. +The method must be used after the first bulk copy is performed and before the next bulk copy's order hint is defined. > [!IMPORTANT] > This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). @@ -158,12 +158,12 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. method is most commonly used when you use a single +The method is most commonly used when you use a single instance to process more than one bulk copy operation. If you create column order hints for one bulk copy operation, you must clear the - after the method and before processing the next bulk copy. + after the method and before processing the next bulk copy. You can clear the entire collection by using the - method, or remove hints individually using the -method or the method. + method, or remove hints individually using the +method or the method. Performing several bulk copies using the same instance will usually be more efficient from a performance point of view than using a separate for each operation. @@ -189,12 +189,12 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. method is most commonly used when you use a single +The method is most commonly used when you use a single instance to process more than one bulk copy operation. If you create column order hints for one bulk copy operation, you must clear the - after the method and before processing the next bulk copy. + after the method and before processing the next bulk copy. You can clear the entire collection by using the - method, or remove hints individually using the -method or the method. + method, or remove hints individually using the +method or the method. Performing several bulk copies using the same instance will usually be more efficient from a performance point of view than using a separate for each operation. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs index 447d964727..a5b6f1f91a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Data; using System.Data.Common; -using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { @@ -12,7 +12,7 @@ public class OrderHint { private static readonly string destinationTable = null; private static readonly string sourceTable = "Customers"; - private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; + private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50)); CREATE CLUSTERED INDEX IX_Test_Table_Customer_ID ON {1} (CustomerID DESC)"; private static readonly string sourceQueryTemplate = "SELECT CustomerId, CompanyName, ContactName FROM {0}"; private static readonly string getRowCountQueryTemplate = "SELECT COUNT(*) FROM {0}"; @@ -20,7 +20,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) { dstTable = destinationTable != null ? destinationTable : dstTable; string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); - string initialQuery = string.Format(initialQueryTemplate, dstTable); + string initialQuery = string.Format(initialQueryTemplate, dstTable, dstTable); string getRowCountQuery = string.Format(getRowCountQueryTemplate, sourceTable); using (SqlConnection dstConn = new SqlConnection(dstConstr)) @@ -36,51 +36,72 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) { int nRowsInSource = Convert.ToInt32(srcCmd.ExecuteScalar()); srcCmd.CommandText = sourceQuery; - using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn)) + using (SqlBulkCopy bulkCopy = new SqlBulkCopy(dstConn)) { - bulkcopy.DestinationTableName = dstTable; + bulkCopy.DestinationTableName = dstTable; // no hints using (DbDataReader reader = srcCmd.ExecuteReader()) { - bulkcopy.WriteToServer(reader); - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource); + bulkCopy.WriteToServer(reader); } // hint for 1 of 3 columns using (DbDataReader reader = srcCmd.ExecuteReader()) { - bulkcopy.ColumnOrderHints.Add("CustomerID", SortOrder.Ascending); - bulkcopy.WriteToServer(reader); - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 2); + bulkCopy.ColumnOrderHints.Add("CustomerID", SortOrder.Ascending); + bulkCopy.WriteToServer(reader); } // hints for all 3 columns // order of hints is not the same as column order in table using (DbDataReader reader = srcCmd.ExecuteReader()) { - bulkcopy.ColumnOrderHints.Add("ContactName", SortOrder.Descending); - bulkcopy.ColumnOrderHints.Add("CompanyName", SortOrder.Ascending); - bulkcopy.WriteToServer(reader); - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 3); + bulkCopy.ColumnOrderHints.Add("ContactName", SortOrder.Descending); + bulkCopy.ColumnOrderHints.Add("CompanyName", SortOrder.Ascending); + bulkCopy.WriteToServer(reader); } // add column mappings using (DbDataReader reader = srcCmd.ExecuteReader()) { - bulkcopy.ColumnMappings.Add(0, 1); - bulkcopy.ColumnMappings.Add(1, 2); - bulkcopy.ColumnMappings.Add(2, 0); - bulkcopy.WriteToServer(reader); - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 4); + bulkCopy.ColumnMappings.Add(0, 1); + bulkCopy.ColumnMappings.Add(1, 2); + bulkCopy.ColumnMappings.Add(2, 0); + bulkCopy.WriteToServer(reader); + } + + // WriteToServer DataTable overload + using (SqlDataAdapter dataAdapter = new SqlDataAdapter(srcCmd)) + { + DataTable dataTable = new DataTable(); + dataAdapter.Fill(dataTable); + bulkCopy.WriteToServer(dataTable); + } + + // WriteToServer DataRow[] overload + using (SqlDataAdapter dataAdapter = new SqlDataAdapter(srcCmd)) + { + DataTable dataTable = new DataTable(); + dataAdapter.Fill(dataTable); + bulkCopy.WriteToServer(dataTable.Select()); + } + + // WriteToServer DataTable, DataRowState overload + using (SqlDataAdapter dataAdapter = new SqlDataAdapter(srcCmd)) + { + DataTable dataTable = new DataTable(); + dataAdapter.Fill(dataTable); + DataRow[] x = dataTable.Select(); + bulkCopy.WriteToServer(dataTable, DataRowState.Unchanged); } } // add copy options SqlBulkCopyOptions copyOptions = - SqlBulkCopyOptions.AllowEncryptedValueModifications - | SqlBulkCopyOptions.FireTriggers - | SqlBulkCopyOptions.KeepNulls; + SqlBulkCopyOptions.AllowEncryptedValueModifications | + SqlBulkCopyOptions.FireTriggers | + SqlBulkCopyOptions.KeepNulls; using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn, copyOptions, null)) { bulkcopy.DestinationTableName = dstTable; @@ -90,13 +111,15 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) using (DbDataReader reader = srcCmd.ExecuteReader()) { bulkcopy.WriteToServer(reader); - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 5); } } + + const int nWriteToServerCalls = 8; + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * nWriteToServerCalls); } finally { - Helpers.TryExecute(dstCmd, "drop table " + dstTable); + DataTestUtility.DropTable(dstConn, dstTable); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs index cee0f03c49..4707229d96 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs @@ -103,7 +103,7 @@ public static async Task TestAsync(string srcConstr, string dstConstr, string ds } finally { - Helpers.TryExecute(dstCmd, "drop table " + dstTable); + DataTestUtility.DropTable(dstConn, dstTable); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs index b0c247ce95..594c416b63 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs @@ -49,7 +49,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) } finally { - Helpers.TryExecute(dstCmd, "drop table " + dstTable); + DataTestUtility.DropTable(dstConn, dstTable); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs index b0fd10e3f7..34be4b76d2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs @@ -59,7 +59,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) } finally { - Helpers.TryExecute(dstCmd, "drop table " + dstTable); + DataTestUtility.DropTable(dstConn, dstTable); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs index 10bc0ec95b..db0bf0bef7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs @@ -65,7 +65,7 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) } finally { - Helpers.TryExecute(dstCmd, "drop table " + dstTable); + DataTestUtility.DropTable(dstConn, dstTable); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs index cc5b743844..6d3b4103a9 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs @@ -23,12 +23,14 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) using (SqlCommand dstCmd = dstConn.CreateCommand()) { dstConn.Open(); + SqlTransaction txn = dstConn.BeginTransaction(); + dstCmd.Transaction = txn; Helpers.TryExecute(dstCmd, initialQuery); + using (SqlConnection srcConn = new SqlConnection(srcConstr)) using (SqlCommand srcCmd = new SqlCommand(sourceQuery, srcConn)) { srcConn.Open(); - SqlTransaction txn = dstConn.BeginTransaction(); using (DbDataReader reader = srcCmd.ExecuteReader()) { using (SqlBulkCopy bulkcopy = new SqlBulkCopy( From 5328079f0c7133632f964d540ced6cacfe56e0eb Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Wed, 29 Apr 2020 11:06:24 -0700 Subject: [PATCH 07/34] assigned task number to order hint identity column test --- src/Microsoft.Data.SqlClient.sln | 4 ++-- .../tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 8587c6a397..2d9639f8b5 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -767,13 +767,13 @@ Global {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs index fd9d86598d..82540f2738 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs @@ -279,7 +279,7 @@ public void OrderHintAsyncTest() } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] - [ActiveIssue(1)] + [ActiveIssue(12219)] public void OrderHintIdentityColumnTest() { OrderHintIdentityColumn.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintIdentityColumn")); From d88cb0335c1c51aeb67e701f8778fda833c090fc Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Sat, 9 May 2020 00:09:23 -0700 Subject: [PATCH 08/34] Collection class reworked to handle duplicate columns --- .../SqlBulkCopyColumnOrderHintCollection.xml | 4 +- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 17 +-- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 39 +++--- .../SqlClient/SqlBulkCopyColumnOrderHint.cs | 24 ++-- .../SqlBulkCopyColumnOrderHintCollection.cs | 73 ++++++++++- ...qlBulkCopyColumnOrderHintCollectionTest.cs | 122 +++++++++++++++--- .../SQL/SqlBulkCopyTest/OrderHint.cs | 12 +- .../SQL/SqlBulkCopyTest/OrderHintAsync.cs | 10 +- .../OrderHintDuplicateColumn.cs | 40 +++--- .../OrderHintIdentityColumn.cs | 7 +- .../OrderHintMissingTargetColumn.cs | 14 +- .../SqlBulkCopyTest/OrderHintTransaction.cs | 6 +- .../SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs | 25 ++-- 13 files changed, 270 insertions(+), 123 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index 796c98cd31..e644b7d22b 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -40,8 +40,8 @@ use a Transact-SQL `INSERT … SELECT` statement to copy the data. - - The object that describes the order hint to be added to the collection. + + The object that describes the order hint to be added to the collection. Adds the specified order hint to the . A object. diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index ab9d511134..d4648f78f6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -829,29 +829,20 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i private string TryGetOrderHintText() { StringBuilder orderHintText = new StringBuilder("ORDER("); - HashSet columnNames = new HashSet(); - foreach (SqlBulkCopyColumnOrderHint columnOrderHint in ColumnOrderHints) + foreach (SqlBulkCopyColumnOrderHint orderHint in ColumnOrderHints) { - string columnNameArg = columnOrderHint.Column; - + string columnNameArg = orderHint.Column; if (!_destColumnNames.Contains(columnNameArg)) { // column is not valid in the destination table throw SQL.BulkLoadOrderHintInvalidColumn(columnNameArg); } - - if (!columnNames.Contains(columnNameArg)) + if (!string.IsNullOrEmpty(columnNameArg)) { string columnNameEscaped = SqlServerEscapeHelper.EscapeIdentifier(SqlServerEscapeHelper.EscapeStringAsLiteral(columnNameArg)); - string sortOrderText = columnOrderHint.SortOrder == SortOrder.Descending ? "DESC" : "ASC"; + string sortOrderText = orderHint.SortOrder == SortOrder.Descending ? "DESC" : "ASC"; orderHintText.Append($"{columnNameEscaped} {sortOrderText}, "); - columnNames.Add(columnNameArg); - } - else - { - // duplicate column name - throw SQL.BulkLoadOrderHintDuplicateColumn(columnNameArg); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index ece3e77798..b22727b780 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -931,29 +931,20 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i private string TryGetOrderHintText() { StringBuilder orderHintText = new StringBuilder("ORDER("); - HashSet columnNames = new HashSet(); - foreach (SqlBulkCopyColumnOrderHint columnOrderHint in ColumnOrderHints) + foreach (SqlBulkCopyColumnOrderHint orderHint in ColumnOrderHints) { - string columnNameArg = columnOrderHint.Column; - + string columnNameArg = orderHint.Column; if (!_destColumnNames.Contains(columnNameArg)) { // column is not valid in the destination table throw SQL.BulkLoadOrderHintInvalidColumn(columnNameArg); } - - if (!columnNames.Contains(columnNameArg)) + if (!string.IsNullOrEmpty(columnNameArg)) { string columnNameEscaped = SqlServerEscapeHelper.EscapeIdentifier(SqlServerEscapeHelper.EscapeStringAsLiteral(columnNameArg)); - string sortOrderText = columnOrderHint.SortOrder == SortOrder.Descending ? "DESC" : "ASC"; + string sortOrderText = orderHint.SortOrder == SortOrder.Descending ? "DESC" : "ASC"; orderHintText.Append($"{columnNameEscaped} {sortOrderText}, "); - columnNames.Add(columnNameArg); - } - else - { - // duplicate column name - throw SQL.BulkLoadOrderHintDuplicateColumn(columnNameArg); } } @@ -2985,18 +2976,18 @@ private void CopyBatchesAsyncContinuedOnError(bool cleanupParser) { tdsReliabilitySection.Start(); #endif //DEBUG - if ((cleanupParser) && (_parser != null) && (_stateObj != null)) - { - _parser._asyncWrite = false; - Task task = _parser.WriteBulkCopyDone(_stateObj); - Debug.Assert(task == null, "Write should not pend when error occurs"); - RunParser(); - } + if ((cleanupParser) && (_parser != null) && (_stateObj != null)) + { + _parser._asyncWrite = false; + Task task = _parser.WriteBulkCopyDone(_stateObj); + Debug.Assert(task == null, "Write should not pend when error occurs"); + RunParser(); + } - if (_stateObj != null) - { - CleanUpStateObject(); - } + if (_stateObj != null) + { + CleanUpStateObject(); + } #if DEBUG } finally diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs index 704fc780df..0f55b6f7b1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; + namespace Microsoft.Data.SqlClient { /// @@ -17,24 +19,21 @@ public SqlBulkCopyColumnOrderHint(string column, SortOrder sortOrder) private string _columnName; private SortOrder _sortOrder; + internal event EventHandler NameChanging; + /// public string Column { - get - { - if (_columnName != null) - { - return _columnName; - } - return string.Empty; - } + get => _columnName ?? string.Empty; set { - if (!string.IsNullOrEmpty(value)) + // Do nothing if column name is the same + if (!string.IsNullOrEmpty(value) && _columnName != value) { + OnNameChanging(value); _columnName = value; } - else + else if (string.IsNullOrEmpty(value)) { throw SQL.BulkLoadNullEmptyColumnName(nameof(Column)); } @@ -58,5 +57,10 @@ public SortOrder SortOrder } } + private void OnNameChanging(string newName) + { + var handler = NameChanging; + handler?.Invoke(this, newName); + } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs index 20eef779af..26f0f701c1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections; +using System.Collections.Generic; namespace Microsoft.Data.SqlClient { @@ -11,20 +13,27 @@ public sealed class SqlBulkCopyColumnOrderHintCollection : CollectionBase { internal bool ReadOnly { get; set; } + private readonly Dictionary _nameToOrderHint = new Dictionary(); + /// public SqlBulkCopyColumnOrderHint this[int index] => (SqlBulkCopyColumnOrderHint)List[index]; - /// - public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint bulkCopyColumnOrderHint) + /// + public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint) { AssertWriteAccess(); - if (string.IsNullOrEmpty(bulkCopyColumnOrderHint.Column) || - bulkCopyColumnOrderHint.SortOrder == SortOrder.Unspecified) + if (columnOrderHint == null) + { + throw new ArgumentNullException(); + } + if (string.IsNullOrEmpty(columnOrderHint.Column) || + columnOrderHint.SortOrder == SortOrder.Unspecified) { throw SQL.BulkLoadInvalidOrderHint(); } - InnerList.Add(bulkCopyColumnOrderHint); - return bulkCopyColumnOrderHint; + RegisterColumnName(columnOrderHint, columnOrderHint.Column); + InnerList.Add(columnOrderHint); + return columnOrderHint; } /// @@ -46,6 +55,10 @@ private void AssertWriteAccess() public new void Clear() { AssertWriteAccess(); + foreach (SqlBulkCopyColumnOrderHint orderHint in InnerList) + { + UnregisterColumnName(orderHint, orderHint.Column); + } base.Clear(); } @@ -62,6 +75,16 @@ private void AssertWriteAccess() public void Insert(int index, SqlBulkCopyColumnOrderHint value) { AssertWriteAccess(); + // Try inserting into an invalid index to throw an exception + if (index < 0 || index > InnerList.Count) + { + InnerList.Insert(index, value); + } + if (value == null) + { + throw new ArgumentNullException(); + } + RegisterColumnName(value, value.Column); InnerList.Insert(index, value); } @@ -69,6 +92,11 @@ public void Insert(int index, SqlBulkCopyColumnOrderHint value) public void Remove(SqlBulkCopyColumnOrderHint value) { AssertWriteAccess(); + if (value == null) + { + throw new ArgumentNullException(); + } + UnregisterColumnName(value, value.Column); InnerList.Remove(value); } @@ -76,9 +104,42 @@ public void Remove(SqlBulkCopyColumnOrderHint value) public new void RemoveAt(int index) { AssertWriteAccess(); + var orderHint = (SqlBulkCopyColumnOrderHint)InnerList[index]; + UnregisterColumnName(orderHint, orderHint.Column); base.RemoveAt(index); } + private void ColumnNameChanging(object sender, string newName) + { + if (sender is SqlBulkCopyColumnOrderHint orderHint) + { + if (_nameToOrderHint.ContainsKey(newName)) + { + throw SQL.BulkLoadOrderHintDuplicateColumn(newName); + } + UnregisterColumnName(orderHint, orderHint.Column); + RegisterColumnName(orderHint, newName); + } + } + + private void RegisterColumnName(SqlBulkCopyColumnOrderHint orderHint, string columnName) + { + if (_nameToOrderHint.ContainsKey(columnName)) + { + throw SQL.BulkLoadOrderHintDuplicateColumn(orderHint.Column); + } + _nameToOrderHint.Add(columnName, orderHint); + orderHint.NameChanging += ColumnNameChanging; + } + + private void UnregisterColumnName(SqlBulkCopyColumnOrderHint orderHint, string columnName) + { + if (Contains(orderHint)) + { + _nameToOrderHint.Remove(columnName); + orderHint.NameChanging -= ColumnNameChanging; + } + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index bfbc81f0cc..08834252c0 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -4,8 +4,9 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; - +using System.Linq; using Xunit; namespace Microsoft.Data.SqlClient.Tests @@ -45,20 +46,10 @@ public void Methods_NullParameterPassed_ThrowsArgumentNullException() collection.Add(new SqlBulkCopyColumnOrderHint("column", SortOrder.Ascending)); Assert.Throws(() => collection.CopyTo(null, 0)); + Assert.Throws(() => collection.Add(null)); - // Passing null to the public Add method should really throw ArgumentNullException - // (which would be consistent with the explicit implementation of IList.Add), but - // the full framework does not check for null in the public Add method. Instead it - // accesses the parameter without first checking for null, resulting in - // NullReferenceExpcetion being thrown. - Assert.Throws(() => collection.Add(null)); - - // Passing null to the public Insert and Remove methods should really throw - // ArgumentNullException (which would be consistent with the explicit - // implementations of IList.Insert and IList.Remove), but the full framework - // does not check for null in these methods. - collection.Insert(0, null); - collection.Remove(null); + Assert.Throws(() => collection.Insert(0, null)); + Assert.Throws(() => collection.Remove(null)); IList list = collection; Assert.Throws(() => list[0] = null); @@ -126,7 +117,7 @@ public void Add_HelperOverloads_ItemsAddedAsExpected() } [Fact] - public void Add_InvalidItems_ThrowsInvalidOperationException() + public void Add_InvalidItems_ThrowsArgumentException() { SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); Assert.Throws(() => collection.Add(new SqlBulkCopyColumnOrderHint(null, SortOrder.Ascending))); @@ -501,5 +492,106 @@ public void SyncRoot_NotNullAndSameObject() Assert.NotNull(collection.SyncRoot); Assert.Same(collection.SyncRoot, collection.SyncRoot); } + + [Fact] + public void Add_DuplicateColumnNames_NotAllowed() + { + SqlBulkCopyColumnOrderHintCollection collection1 = CreateCollection(); + SqlBulkCopyColumnOrderHintCollection collection2 = CreateCollection(); + + var item1 = new SqlBulkCopyColumnOrderHint("column", SortOrder.Ascending); + item1.Column = "column"; + item1.Column = "column1"; + Assert.Equal("column1", item1.Column); + + collection1.Add(item1); + collection1[0].Column += "2"; + item1.Column += "3"; + Assert.Equal("column123", item1.Column); + + collection2.Add(item1); + item1.Column += "4"; + Assert.Equal("column1234", collection1[0].Column); + Assert.Equal("column1234", collection2[0].Column); + + item1.Column = "column1"; + collection1.Add("column2", SortOrder.Ascending); + TryAddingDuplicates(collection1, item1, initialCount: 2); + + Assert.Throws(() => collection1.Add(item1)); + var item2 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + collection1.Add(item2); + Assert.Throws(() => item2.Column = "column2"); + var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + Assert.Throws(() => collection1.Add(item3)); + TryAddingDuplicates(collection1, item1, initialCount: 3); + + collection1.Add("column4", SortOrder.Ascending); + Assert.Throws(() => collection1.Add("column1", SortOrder.Ascending)); + Assert.Throws(() => collection1.Add("column2", SortOrder.Ascending)); + Assert.Throws(() => collection1.Add("column3", SortOrder.Ascending)); + TryAddingDuplicates(collection1, item1, initialCount: 4); + + collection2.Insert(collection2.Count,item2); + item3.Column = "column5"; + collection2.Insert(collection2.Count,item3); + Assert.Throws(() => collection1[collection1.IndexOf(item2)].Column=item3.Column); + Assert.Throws(() => collection2[collection2.IndexOf(item2)].Column=item3.Column); + TryAddingDuplicates(collection2, item2, initialCount: 3); + + collection2.Remove(item2); + collection2[collection2.IndexOf(item3)].Column = item2.Column; + Assert.Throws(() => collection1[collection1.IndexOf(item1)].Column = item2.Column); + + collection1.Clear(); + Assert.Empty(collection1); + } + + // tries to add duplicate column names using different methods + private void TryAddingDuplicates(SqlBulkCopyColumnOrderHintCollection collection, SqlBulkCopyColumnOrderHint orderHint, int initialCount) + { + string initialName = orderHint.Column; + string validName = "valid name"; + string invalidName = collection[collection.Count - 1].Column; + SqlBulkCopyColumnOrderHint newHint = new SqlBulkCopyColumnOrderHint(invalidName, SortOrder.Ascending); + + Assert.Throws(() => orderHint.Column = invalidName); + Assert.Throws(() => collection.Add(orderHint)); + Assert.Throws(() => collection.Add(newHint)); + Assert.Throws(() => collection.Add(orderHint.Column, SortOrder.Ascending)); + Assert.Throws(() => collection.Add(invalidName, SortOrder.Ascending)); + Assert.Throws(() => collection.Insert(0, orderHint)); + Assert.Throws(() => collection.Insert(collection.Count, newHint)); + + collection.Insert(0, new SqlBulkCopyColumnOrderHint(validName, SortOrder.Ascending)); + Assert.Throws(() => orderHint.Column = validName); + Assert.Throws(() => collection[0].Column = invalidName); + + collection.RemoveAt(0); + orderHint.Column = validName; + collection[collection.IndexOf(orderHint)].Column = validName; + + ValidateCollection(collection, initialCount); + orderHint.Column = initialName; + } + + // verifies that the collection contains no duplicate column names + private bool ValidateCollection(SqlBulkCopyColumnOrderHintCollection collection, int expectedCount) + { + Assert.Equal(expectedCount, collection.Count); + HashSet columnNames = new HashSet(); + foreach (SqlBulkCopyColumnOrderHint orderHint in collection) + { + if (columnNames.Contains(orderHint.Column)) + { + return false; + } + else + { + columnNames.Add(orderHint.Column); + } + } + return true; + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs index a5b6f1f91a..ff3573d529 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs @@ -10,19 +10,19 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class OrderHint { - private static readonly string destinationTable = null; private static readonly string sourceTable = "Customers"; - private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50)); CREATE CLUSTERED INDEX IX_Test_Table_Customer_ID ON {1} (CustomerID DESC)"; + private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50)); CREATE CLUSTERED INDEX IX_Test_Table_Customer_ID ON {0} (CustomerID DESC)"; private static readonly string sourceQueryTemplate = "SELECT CustomerId, CompanyName, ContactName FROM {0}"; private static readonly string getRowCountQueryTemplate = "SELECT COUNT(*) FROM {0}"; - public static void Test(string srcConstr, string dstConstr, string dstTable) + public static void Test(string srcConstr, string dstTable) { - dstTable = destinationTable != null ? destinationTable : dstTable; + srcConstr = (new SqlConnectionStringBuilder(srcConstr) { InitialCatalog = "Northwind" }).ConnectionString; + string dstConstr = (new SqlConnectionStringBuilder(srcConstr)).ConnectionString; string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); - string initialQuery = string.Format(initialQueryTemplate, dstTable, dstTable); + string initialQuery = string.Format(initialQueryTemplate, dstTable); string getRowCountQuery = string.Format(getRowCountQueryTemplate, sourceTable); - + using (SqlConnection dstConn = new SqlConnection(dstConstr)) using (SqlCommand dstCmd = dstConn.CreateCommand()) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs index 4707229d96..5fbcdca169 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs @@ -11,22 +11,22 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class OrderHintAsync { - private static readonly string destinationTable = null; private static readonly string sourceTable = "Customers"; private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; private static readonly string sourceQueryTemplate = "SELECT CustomerId, CompanyName, ContactName FROM {0}"; private static readonly string getRowCountQueryTemplate = "SELECT COUNT(*) FROM {0}"; - public static void Test(string srcConstr, string dstConstr, string dstTable) + public static void Test(string srcConstr, string dstTable) { - Task t = TestAsync(srcConstr, dstConstr, dstTable); + Task t = TestAsync(srcConstr, dstTable); t.Wait(); Assert.True(t.IsCompleted, "Task did not complete! Status: " + t.Status); } - public static async Task TestAsync(string srcConstr, string dstConstr, string dstTable) + public static async Task TestAsync(string srcConstr, string dstTable) { - dstTable = destinationTable != null ? destinationTable : dstTable; + srcConstr = (new SqlConnectionStringBuilder(srcConstr) { InitialCatalog = "Northwind" }).ConnectionString; + string dstConstr = (new SqlConnectionStringBuilder(srcConstr)).ConnectionString; string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); string initialQuery = string.Format(initialQueryTemplate, dstTable); string getRowCountQuery = string.Format(getRowCountQueryTemplate, sourceTable); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs index 594c416b63..59e82a8a97 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintDuplicateColumn.cs @@ -4,21 +4,24 @@ using System; using System.Data.Common; +using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class OrderHintDuplicateColumn { - private static readonly string destinationTable = null; private static readonly string sourceTable = "Customers"; private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; private static readonly string sourceQueryTemplate = "SELECT CustomerID, CompanyName, ContactName FROM {0}"; + private static readonly string getRowCountQueryTemplate = "SELECT COUNT(*) FROM {0}"; - public static void Test(string srcConstr, string dstConstr, string dstTable) + public static void Test(string srcConstr, string dstTable) { - dstTable = destinationTable != null ? destinationTable : dstTable; + srcConstr = (new SqlConnectionStringBuilder(srcConstr) { InitialCatalog = "Northwind" }).ConnectionString; + string dstConstr = (new SqlConnectionStringBuilder(srcConstr)).ConnectionString; string initialQuery = string.Format(initialQueryTemplate, dstTable); string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); + string getRowCountQuery = string.Format(getRowCountQueryTemplate, sourceTable); using (SqlConnection dstConn = new SqlConnection(dstConstr)) using (SqlCommand dstCmd = dstConn.CreateCommand()) @@ -26,33 +29,40 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) dstConn.Open(); Helpers.TryExecute(dstCmd, initialQuery); using (SqlConnection srcConn = new SqlConnection(srcConstr)) - using (SqlCommand srcCmd = new SqlCommand(sourceQuery, srcConn)) + using (SqlCommand srcCmd = new SqlCommand(getRowCountQuery, srcConn)) { srcConn.Open(); - - using (DbDataReader reader = srcCmd.ExecuteReader()) + try { - using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn)) + int nRowsInSource = Convert.ToInt32(srcCmd.ExecuteScalar()); + srcCmd.CommandText = sourceQuery; + using (DbDataReader reader = srcCmd.ExecuteReader()) { - try + using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn)) { bulkcopy.DestinationTableName = dstTable; - string destColumn = "CompanyName"; + const string destColumn = "CompanyName"; + const string destColumn2 = "ContactName"; bulkcopy.ColumnOrderHints.Add(destColumn, SortOrder.Ascending); - bulkcopy.ColumnOrderHints.Add(destColumn, SortOrder.Descending); string expectedErrorMsg = string.Format( SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintDuplicateColumn, destColumn); DataTestUtility.AssertThrowsWrapper( - () => bulkcopy.WriteToServer(reader), + () => bulkcopy.ColumnOrderHints.Add(destColumn, SortOrder.Ascending), exceptionMessage: expectedErrorMsg); - } - finally - { - DataTestUtility.DropTable(dstConn, dstTable); + + bulkcopy.ColumnOrderHints.Add(destColumn2, SortOrder.Ascending); + Assert.Equal(2, bulkcopy.ColumnOrderHints.Count); + + bulkcopy.WriteToServer(reader); + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource); } } } + finally + { + DataTestUtility.DropTable(dstConn, dstTable); + } } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs index 34be4b76d2..a95c13ad36 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintIdentityColumn.cs @@ -4,21 +4,20 @@ using System; using System.Data.Common; -using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class OrderHintIdentityColumn { - private static readonly string destinationTable = null; private static readonly string sourceTable = "Customers"; private static readonly string initialQueryTemplate = "create table {0} (CustomerID int IDENTITY NOT NULL, CompanyName nvarchar(50), ContactName nvarchar(50))"; private static readonly string sourceQueryTemplate = "SELECT CustomerId, CompanyName, ContactName FROM {0}"; private static readonly string getRowCountQueryTemplate = "SELECT COUNT(*) FROM {0}"; - public static void Test(string srcConstr, string dstConstr, string dstTable) + public static void Test(string srcConstr, string dstTable) { - dstTable = destinationTable != null ? destinationTable : dstTable; + srcConstr = (new SqlConnectionStringBuilder(srcConstr) { InitialCatalog = "Northwind" }).ConnectionString; + string dstConstr = (new SqlConnectionStringBuilder(srcConstr)).ConnectionString; string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); string initialQuery = string.Format(initialQueryTemplate, dstTable); string getRowCountQuery = string.Format(getRowCountQueryTemplate, sourceTable); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs index 8b26579201..dcfeb6e311 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintMissingTargetColumn.cs @@ -10,14 +10,14 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class OrderHintMissingTargetColumn { - private static readonly string destinationTable = null; private static readonly string sourceTable = "Customers"; private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; private static readonly string sourceQueryTemplate = "SELECT CustomerID, CompanyName, ContactName FROM {0}"; - public static void Test(string srcConstr, string dstConstr, string dstTable) + public static void Test(string srcConstr, string dstTable) { - dstTable = destinationTable != null ? destinationTable : dstTable; + srcConstr = (new SqlConnectionStringBuilder(srcConstr) { InitialCatalog = "Northwind" }).ConnectionString; + string dstConstr = (new SqlConnectionStringBuilder(srcConstr)).ConnectionString; string initialQuery = string.Format(initialQueryTemplate, dstTable); string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); @@ -37,9 +37,9 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) try { bulkcopy.DestinationTableName = dstTable; - string nonexistentColumn = "nonexistent column"; - string sourceColumn = "CustomerID"; - string destColumn = "ContactName"; + const string nonexistentColumn = "nonexistent column"; + const string sourceColumn = "CustomerID"; + const string destColumn = "ContactName"; // column does not exist in destination table bulkcopy.ColumnOrderHints.Add(nonexistentColumn, SortOrder.Ascending); @@ -51,13 +51,13 @@ public static void Test(string srcConstr, string dstConstr, string dstTable) exceptionMessage: expectedErrorMsg); // column does not exist in destination table because of user-defined mapping + bulkcopy.ColumnMappings.Add(sourceColumn, destColumn); bulkcopy.ColumnOrderHints.RemoveAt(0); bulkcopy.ColumnOrderHints.Add(sourceColumn, SortOrder.Ascending); Assert.True(bulkcopy.ColumnOrderHints.Count == 1, "Error adding a column order hint"); expectedErrorMsg = string.Format( SystemDataResourceManager.Instance.SQL_BulkLoadOrderHintInvalidColumn, sourceColumn); - bulkcopy.ColumnMappings.Add(sourceColumn, destColumn); DataTestUtility.AssertThrowsWrapper( () => bulkcopy.WriteToServer(reader), exceptionMessage: expectedErrorMsg); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs index 6d3b4103a9..73dae137aa 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintTransaction.cs @@ -8,14 +8,14 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { class OrderHintTransaction { - private static readonly string destinationTable = null; private static readonly string sourceTable = "Customers"; private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; private static readonly string sourceQueryTemplate = "SELECT CustomerID, CompanyName, ContactName FROM {0}"; - public static void Test(string srcConstr, string dstConstr, string dstTable) + public static void Test(string srcConstr, string dstTable) { - dstTable = destinationTable != null ? destinationTable : dstTable; + srcConstr = (new SqlConnectionStringBuilder(srcConstr) { InitialCatalog = "Northwind" }).ConnectionString; + string dstConstr = (new SqlConnectionStringBuilder(srcConstr)).ConnectionString; string initialQuery = string.Format(initialQueryTemplate, dstTable); string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs index 82540f2738..77c7ff75ba 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs @@ -248,42 +248,41 @@ public void DestinationTableNameWithSpecialCharTest() DestinationTableNameWithSpecialChar.Test(srcConstr, AddGuid("SqlBulkCopyTest_DestinationTableNameWithSpecialChar")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public void OrderHintTest() { - OrderHint.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHint")); + OrderHint.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHint")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public void OrderHintMissingTargetColumnTest() { - OrderHintMissingTargetColumn.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintMissingTargetColumn")); + OrderHintMissingTargetColumn.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHintMissingTargetColumn")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public void OrderHintDuplicateColumnTest() { - OrderHintDuplicateColumn.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintDuplicateColumn")); + OrderHintDuplicateColumn.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHintDuplicateColumn")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public void OrderHintTransactionTest() { - OrderHintTransaction.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintTransaction")); + OrderHintTransaction.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHintTransaction")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public void OrderHintAsyncTest() { - OrderHintAsync.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintAsync")); + OrderHintAsync.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHintAsync")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [ActiveIssue(12219)] public void OrderHintIdentityColumnTest() { - OrderHintIdentityColumn.Test(srcConstr, dstConstr, AddGuid("SqlBulkCopyTest_OrderHintIdentityColumn")); + OrderHintIdentityColumn.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHintIdentityColumn")); } - } } From d7f3deb98ee8d79aa8e66bc01d03c5fdffa0628e Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Sat, 9 May 2020 00:12:30 -0700 Subject: [PATCH 09/34] spacing --- .../Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs index 26f0f701c1..d7c988d5b9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs @@ -142,4 +142,3 @@ private void UnregisterColumnName(SqlBulkCopyColumnOrderHint orderHint, string c } } } - From 9e737cd363d74a2a1845f423525ff404585c377c Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Sun, 10 May 2020 23:50:36 -0700 Subject: [PATCH 10/34] Updated tests for duplicate column names, updated documentation --- .../Microsoft.Data.SqlClient/SqlBulkCopy.xml | 19 +++++++++++ .../SqlBulkCopyColumnOrderHintCollection.cs | 10 +++--- ...qlBulkCopyColumnOrderHintCollectionTest.cs | 18 +++++----- .../SQL/SqlBulkCopyTest/OrderHintAsync.cs | 33 +++++++++---------- 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml index 31e025fa4e..dd976d5b27 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml @@ -360,6 +360,25 @@ During the execution of a bulk copy operation, this collection can be accessed, ]]> + + + Returns a collection of + + items. Column order hints describe the sort order of columns in the clustered index of the destination table. + + + A collection of column order hints. By default, it is an empty collection. + + + + + Name of the destination table on the server. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs index d7c988d5b9..094b28360a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs @@ -13,7 +13,7 @@ public sealed class SqlBulkCopyColumnOrderHintCollection : CollectionBase { internal bool ReadOnly { get; set; } - private readonly Dictionary _nameToOrderHint = new Dictionary(); + private readonly HashSet _columnNames = new HashSet(); /// public SqlBulkCopyColumnOrderHint this[int index] => (SqlBulkCopyColumnOrderHint)List[index]; @@ -113,7 +113,7 @@ private void ColumnNameChanging(object sender, string newName) { if (sender is SqlBulkCopyColumnOrderHint orderHint) { - if (_nameToOrderHint.ContainsKey(newName)) + if (_columnNames.Contains(newName)) { throw SQL.BulkLoadOrderHintDuplicateColumn(newName); } @@ -124,11 +124,11 @@ private void ColumnNameChanging(object sender, string newName) private void RegisterColumnName(SqlBulkCopyColumnOrderHint orderHint, string columnName) { - if (_nameToOrderHint.ContainsKey(columnName)) + if (_columnNames.Contains(columnName)) { throw SQL.BulkLoadOrderHintDuplicateColumn(orderHint.Column); } - _nameToOrderHint.Add(columnName, orderHint); + _columnNames.Add(columnName); orderHint.NameChanging += ColumnNameChanging; } @@ -136,7 +136,7 @@ private void UnregisterColumnName(SqlBulkCopyColumnOrderHint orderHint, string c { if (Contains(orderHint)) { - _nameToOrderHint.Remove(columnName); + _columnNames.Remove(columnName); orderHint.NameChanging -= ColumnNameChanging; } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index 08834252c0..49027c3689 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -532,17 +532,17 @@ public void Add_DuplicateColumnNames_NotAllowed() Assert.Throws(() => collection1.Add("column3", SortOrder.Ascending)); TryAddingDuplicates(collection1, item1, initialCount: 4); - collection2.Insert(collection2.Count,item2); + collection2.Insert(collection2.Count, item2); item3.Column = "column5"; - collection2.Insert(collection2.Count,item3); - Assert.Throws(() => collection1[collection1.IndexOf(item2)].Column=item3.Column); - Assert.Throws(() => collection2[collection2.IndexOf(item2)].Column=item3.Column); + collection2.Insert(collection2.Count, item3); + Assert.Throws(() => collection1[collection1.IndexOf(item2)].Column = item3.Column); + Assert.Throws(() => collection2[collection2.IndexOf(item2)].Column = item3.Column); TryAddingDuplicates(collection2, item2, initialCount: 3); collection2.Remove(item2); collection2[collection2.IndexOf(item3)].Column = item2.Column; Assert.Throws(() => collection1[collection1.IndexOf(item1)].Column = item2.Column); - + collection1.Clear(); Assert.Empty(collection1); } @@ -571,7 +571,7 @@ private void TryAddingDuplicates(SqlBulkCopyColumnOrderHintCollection collection orderHint.Column = validName; collection[collection.IndexOf(orderHint)].Column = validName; - ValidateCollection(collection, initialCount); + Assert.True(ValidateCollection(collection, initialCount)); orderHint.Column = initialName; } @@ -582,13 +582,13 @@ private bool ValidateCollection(SqlBulkCopyColumnOrderHintCollection collection, HashSet columnNames = new HashSet(); foreach (SqlBulkCopyColumnOrderHint orderHint in collection) { - if (columnNames.Contains(orderHint.Column)) + if (!columnNames.Contains(orderHint.Column)) { - return false; + columnNames.Add(orderHint.Column); } else { - columnNames.Add(orderHint.Column); + return false; } } return true; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs index 5fbcdca169..d477828029 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Data; using System.Data.Common; using System.Threading.Tasks; using Xunit; @@ -44,43 +45,39 @@ public static async Task TestAsync(string srcConstr, string dstTable) { int nRowsInSource = Convert.ToInt32(await srcCmd.ExecuteScalarAsync()); srcCmd.CommandText = sourceQuery; - using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dstConn)) + using (SqlBulkCopy bulkCopy = new SqlBulkCopy(dstConn)) { - bulkcopy.DestinationTableName = dstTable; + bulkCopy.DestinationTableName = dstTable; // no hints using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) { - await bulkcopy.WriteToServerAsync(reader); - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource); + await bulkCopy.WriteToServerAsync(reader); } // hint for 1 of 3 columns using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) { - bulkcopy.ColumnOrderHints.Add("CustomerID", SortOrder.Ascending); - await bulkcopy.WriteToServerAsync(reader); - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 2); + bulkCopy.ColumnOrderHints.Add("CustomerID", SortOrder.Ascending); + await bulkCopy.WriteToServerAsync(reader); } // hints for all 3 columns // order of hints is not the same as column order in table using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) { - bulkcopy.ColumnOrderHints.Add("ContactName", SortOrder.Descending); - bulkcopy.ColumnOrderHints.Add("CompanyName", SortOrder.Ascending); - await bulkcopy.WriteToServerAsync(reader); - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 3); + bulkCopy.ColumnOrderHints.Add("ContactName", SortOrder.Descending); + bulkCopy.ColumnOrderHints.Add("CompanyName", SortOrder.Ascending); + await bulkCopy.WriteToServerAsync(reader); } // add column mappings using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) { - bulkcopy.ColumnMappings.Add(0, 1); - bulkcopy.ColumnMappings.Add(1, 2); - bulkcopy.ColumnMappings.Add(2, 0); - await bulkcopy.WriteToServerAsync(reader); - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 4); + bulkCopy.ColumnMappings.Add(0, 1); + bulkCopy.ColumnMappings.Add(1, 2); + bulkCopy.ColumnMappings.Add(2, 0); + await bulkCopy.WriteToServerAsync(reader); } } @@ -97,9 +94,11 @@ public static async Task TestAsync(string srcConstr, string dstTable) using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) { await bulkcopy.WriteToServerAsync(reader); - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * 5); } } + + const int nWriteToServerCalls = 5; + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * nWriteToServerCalls); } finally { From 4cd38d5fec82cb25783750c95c4bbfd874e96cea Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Mon, 11 May 2020 16:42:25 -0700 Subject: [PATCH 11/34] Added couple asserts --- .../FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index 49027c3689..ccd28fdeb9 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -516,6 +516,8 @@ public void Add_DuplicateColumnNames_NotAllowed() item1.Column = "column1"; collection1.Add("column2", SortOrder.Ascending); + Assert.Throws(() => item1.Column = "column2"); + Assert.Equal("column1", item1.Column); TryAddingDuplicates(collection1, item1, initialCount: 2); Assert.Throws(() => collection1.Add(item1)); From 25f352fd27d6bf8ff6e52354a466884c17a2d894 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Mon, 18 May 2020 15:03:25 -0700 Subject: [PATCH 12/34] Clean up tests --- .../netcore/ref/Microsoft.Data.SqlClient.cs | 37 ++++++ .../netfx/ref/Microsoft.Data.SqlClient.cs | 37 ++++++ ...qlBulkCopyColumnOrderHintCollectionTest.cs | 113 ++++++++---------- 3 files changed, 121 insertions(+), 66 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 60fb125690..0b6ed1ae6c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -141,6 +141,8 @@ public SqlBulkCopy(string connectionString, Microsoft.Data.SqlClient.SqlBulkCopy public bool EnableStreaming { get { throw null; } set { } } /// public Microsoft.Data.SqlClient.SqlBulkCopyColumnMappingCollection ColumnMappings { get { throw null; } } + /// + public SqlBulkCopyColumnOrderHintCollection ColumnOrderHints { get { throw null; } } /// public string DestinationTableName { get { throw null; } set { } } /// @@ -237,6 +239,41 @@ public void Remove(Microsoft.Data.SqlClient.SqlBulkCopyColumnMapping value) { } /// public new void RemoveAt(int index) { } } + /// + public sealed class SqlBulkCopyColumnOrderHint + { + /// + public SqlBulkCopyColumnOrderHint(string column, SortOrder sortOrder) { } + /// + public string Column { get { throw null; } set { } } + /// + public SortOrder SortOrder { get { throw null; } set { } } + internal event System.EventHandler NameChanging; + } + /// + public sealed class SqlBulkCopyColumnOrderHintCollection : System.Collections.CollectionBase + { + /// + public SqlBulkCopyColumnOrderHint this[int index] { get { throw null; } } + /// + public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint) { throw null; } + /// + public SqlBulkCopyColumnOrderHint Add(string column, SortOrder sortOrder) { throw null; } + /// + public new void Clear() { } + /// + public bool Contains(SqlBulkCopyColumnOrderHint value) { throw null; } + /// + public void CopyTo(SqlBulkCopyColumnOrderHint[] array, int index) { } + /// + public int IndexOf(SqlBulkCopyColumnOrderHint value) { throw null; } + /// + public void Insert(int index, SqlBulkCopyColumnOrderHint value) { } + /// + public void Remove(SqlBulkCopyColumnOrderHint value) { } + /// + public new void RemoveAt(int index) { } + } /// [System.FlagsAttribute] public enum SqlBulkCopyOptions diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 44cc4bb3e1..5267cd2f46 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -155,6 +155,8 @@ public SqlBulkCopy(string connectionString, Microsoft.Data.SqlClient.SqlBulkCopy public int BulkCopyTimeout { get { throw null; } set { } } /// public Microsoft.Data.SqlClient.SqlBulkCopyColumnMappingCollection ColumnMappings { get { throw null; } } + /// + public SqlBulkCopyColumnOrderHintCollection ColumnOrderHints { get { throw null; } } /// public string DestinationTableName { get { throw null; } set { } } /// @@ -253,6 +255,41 @@ public void Remove(Microsoft.Data.SqlClient.SqlBulkCopyColumnMapping value) { } /// public new void RemoveAt(int index) { } } + /// + public sealed class SqlBulkCopyColumnOrderHint + { + /// + public SqlBulkCopyColumnOrderHint(string column, SortOrder sortOrder) { } + /// + public string Column { get { throw null; } set { } } + /// + public SortOrder SortOrder { get { throw null; } set { } } + internal event System.EventHandler NameChanging; + } + /// + public sealed class SqlBulkCopyColumnOrderHintCollection : System.Collections.CollectionBase + { + /// + public SqlBulkCopyColumnOrderHint this[int index] { get { throw null; } } + /// + public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint) { throw null; } + /// + public SqlBulkCopyColumnOrderHint Add(string column, SortOrder sortOrder) { throw null; } + /// + public new void Clear() { } + /// + public bool Contains(SqlBulkCopyColumnOrderHint value) { throw null; } + /// + public void CopyTo(SqlBulkCopyColumnOrderHint[] array, int index) { } + /// + public int IndexOf(SqlBulkCopyColumnOrderHint value) { throw null; } + /// + public void Insert(int index, SqlBulkCopyColumnOrderHint value) { } + /// + public void Remove(SqlBulkCopyColumnOrderHint value) { } + /// + public new void RemoveAt(int index) { } + } /// [System.FlagsAttribute] public enum SqlBulkCopyOptions diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index ccd28fdeb9..eec43b16f3 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -498,89 +498,69 @@ public void Add_DuplicateColumnNames_NotAllowed() { SqlBulkCopyColumnOrderHintCollection collection1 = CreateCollection(); SqlBulkCopyColumnOrderHintCollection collection2 = CreateCollection(); - - var item1 = new SqlBulkCopyColumnOrderHint("column", SortOrder.Ascending); - item1.Column = "column"; - item1.Column = "column1"; - Assert.Equal("column1", item1.Column); + var item1 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); collection1.Add(item1); - collection1[0].Column += "2"; - item1.Column += "3"; + item1.Column += "2"; + collection1[0].Column += "3"; Assert.Equal("column123", item1.Column); + Assert.Throws(() => collection1.Add(item1)); collection2.Add(item1); item1.Column += "4"; + Assert.Same(collection1[0], collection2[0]); Assert.Equal("column1234", collection1[0].Column); - Assert.Equal("column1234", collection2[0].Column); item1.Column = "column1"; collection1.Add("column2", SortOrder.Ascending); + item1.Column = "column1"; + collection1[0].Column = "column1"; + collection1[1].Column = "column2"; Assert.Throws(() => item1.Column = "column2"); - Assert.Equal("column1", item1.Column); - TryAddingDuplicates(collection1, item1, initialCount: 2); - - Assert.Throws(() => collection1.Add(item1)); - var item2 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); - collection1.Add(item2); - Assert.Throws(() => item2.Column = "column2"); - var item3 = new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending); + Assert.Throws(() => collection1[0].Column = "column2"); + Assert.Throws(() => collection1[1].Column = "column1"); + Assert.Throws(() => collection2[0].Column = "column2"); + Assert.Equal("column1", collection1[0].Column); + Assert.Equal("column2", collection1[1].Column); + ValidateCollection(collection1, expectedCount: 2); + + Assert.Throws(() => collection2.Add(collection1[0])); + collection2.Add(collection1[1]); + var item3 = new SqlBulkCopyColumnOrderHint("column1", SortOrder.Ascending); Assert.Throws(() => collection1.Add(item3)); - TryAddingDuplicates(collection1, item1, initialCount: 3); - - collection1.Add("column4", SortOrder.Ascending); - Assert.Throws(() => collection1.Add("column1", SortOrder.Ascending)); - Assert.Throws(() => collection1.Add("column2", SortOrder.Ascending)); - Assert.Throws(() => collection1.Add("column3", SortOrder.Ascending)); - TryAddingDuplicates(collection1, item1, initialCount: 4); - - collection2.Insert(collection2.Count, item2); - item3.Column = "column5"; - collection2.Insert(collection2.Count, item3); - Assert.Throws(() => collection1[collection1.IndexOf(item2)].Column = item3.Column); - Assert.Throws(() => collection2[collection2.IndexOf(item2)].Column = item3.Column); - TryAddingDuplicates(collection2, item2, initialCount: 3); - - collection2.Remove(item2); - collection2[collection2.IndexOf(item3)].Column = item2.Column; - Assert.Throws(() => collection1[collection1.IndexOf(item1)].Column = item2.Column); + item3.Column = "column3"; + collection1.Add(item3); + ValidateCollection(collection1, expectedCount: 3); + ValidateCollection(collection2, expectedCount: 2); + + Assert.Throws(() => collection1.Insert(0, new SqlBulkCopyColumnOrderHint("column3", SortOrder.Ascending))); + collection1.Insert(0, new SqlBulkCopyColumnOrderHint("column4", SortOrder.Ascending)); + collection1.Insert(collection1.Count, new SqlBulkCopyColumnOrderHint("column5", SortOrder.Ascending)); + Assert.Throws(() => collection1[collection1.IndexOf(item1)].Column = "column4"); + ValidateCollection(collection1, expectedCount: 5); + + collection2.Remove(item1); + Assert.Throws(() => collection2[0].Column = item1.Column); + collection2[0].Column = "column6"; + ValidateCollection(collection2, expectedCount: 1); collection1.Clear(); Assert.Empty(collection1); - } - - // tries to add duplicate column names using different methods - private void TryAddingDuplicates(SqlBulkCopyColumnOrderHintCollection collection, SqlBulkCopyColumnOrderHint orderHint, int initialCount) - { - string initialName = orderHint.Column; - string validName = "valid name"; - string invalidName = collection[collection.Count - 1].Column; - SqlBulkCopyColumnOrderHint newHint = new SqlBulkCopyColumnOrderHint(invalidName, SortOrder.Ascending); - - Assert.Throws(() => orderHint.Column = invalidName); - Assert.Throws(() => collection.Add(orderHint)); - Assert.Throws(() => collection.Add(newHint)); - Assert.Throws(() => collection.Add(orderHint.Column, SortOrder.Ascending)); - Assert.Throws(() => collection.Add(invalidName, SortOrder.Ascending)); - Assert.Throws(() => collection.Insert(0, orderHint)); - Assert.Throws(() => collection.Insert(collection.Count, newHint)); - - collection.Insert(0, new SqlBulkCopyColumnOrderHint(validName, SortOrder.Ascending)); - Assert.Throws(() => orderHint.Column = validName); - Assert.Throws(() => collection[0].Column = invalidName); - - collection.RemoveAt(0); - orderHint.Column = validName; - collection[collection.IndexOf(orderHint)].Column = validName; - - Assert.True(ValidateCollection(collection, initialCount)); - orderHint.Column = initialName; + collection1.Add("column1", SortOrder.Descending); + collection1.Add("column2", SortOrder.Descending); + collection1.Add("column3", SortOrder.Descending); + collection1.Add("column4", SortOrder.Descending); + collection1.Add("column5", SortOrder.Descending); + collection2[0].Column = collection1[0].Column; + Assert.Throws(() => collection1.Add(collection2[0])); + ValidateCollection(collection1, expectedCount: 5); } // verifies that the collection contains no duplicate column names - private bool ValidateCollection(SqlBulkCopyColumnOrderHintCollection collection, int expectedCount) + private void ValidateCollection(SqlBulkCopyColumnOrderHintCollection collection, int expectedCount) { - Assert.Equal(expectedCount, collection.Count); + Assert.True(expectedCount == collection.Count, "Collection was not the expected size."); + bool valid; HashSet columnNames = new HashSet(); foreach (SqlBulkCopyColumnOrderHint orderHint in collection) { @@ -590,10 +570,11 @@ private bool ValidateCollection(SqlBulkCopyColumnOrderHintCollection collection, } else { - return false; + valid = false; } } - return true; + valid = true; + Assert.True(valid, "Collection contained a duplicate column name."); } } } From 9900f72ed78a4e8abead92aa818d1330625751a5 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Mon, 18 May 2020 16:17:54 -0700 Subject: [PATCH 13/34] Remove NameChanging event in ref --- .../netcore/ref/Microsoft.Data.SqlClient.cs | 1 - .../netfx/ref/Microsoft.Data.SqlClient.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 0b6ed1ae6c..5e3979d755 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -248,7 +248,6 @@ public SqlBulkCopyColumnOrderHint(string column, SortOrder sortOrder) { } public string Column { get { throw null; } set { } } /// public SortOrder SortOrder { get { throw null; } set { } } - internal event System.EventHandler NameChanging; } /// public sealed class SqlBulkCopyColumnOrderHintCollection : System.Collections.CollectionBase diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 5267cd2f46..d3522a9d82 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -264,7 +264,6 @@ public SqlBulkCopyColumnOrderHint(string column, SortOrder sortOrder) { } public string Column { get { throw null; } set { } } /// public SortOrder SortOrder { get { throw null; } set { } } - internal event System.EventHandler NameChanging; } /// public sealed class SqlBulkCopyColumnOrderHintCollection : System.Collections.CollectionBase From b9416ee76401a7670b910fc7c58e4867159005fc Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Tue, 19 May 2020 11:17:58 -0700 Subject: [PATCH 14/34] Update doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml Co-authored-by: David Engel --- doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml index dd976d5b27..6209fbc6cc 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml @@ -374,8 +374,7 @@ During the execution of a bulk copy operation, this collection can be accessed, ## Remarks SqlBulkCopy's performance is improved if the data being imported is sorted according to the clustered index on the table, if any. -If the data is sorted in a different order, that is other than the order of a clustered index key or if there is no clustered index on the table, -the order hint is ignored. +If the data is sorted in an order that differs from the order of a clustered index key or if there is no clustered index on the table, the order hint is ignored. ]]> From 9e4f02d934c173ea12a9bc054f8186a4e5c7ef3c Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Tue, 19 May 2020 11:18:20 -0700 Subject: [PATCH 15/34] Update doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml Co-authored-by: David Engel --- .../Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml index 586b5d5f93..93e979bc25 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml @@ -13,8 +13,7 @@ Column order hints define the sort order of the column in the destination table. SqlBulkCopy's performance is improved if the data being imported is sorted according to the clustered index on the table, if any. -If the data is sorted in a different order, that is other than the order of a clustered index key or if there is no clustered index on the table, -the order hint is ignored. +If the data is sorted in an order that differs from the order of a clustered index key or if there is no clustered index on the table, the order hint is ignored. Order hints can be specified for any number of columns in the destination table. By default, the bulk insert operation assumes the data is unordered if no hints are provided. From cf680d98c273c768ea6ef572d2ae7a7223320587 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Tue, 19 May 2020 11:18:28 -0700 Subject: [PATCH 16/34] Update doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml Co-authored-by: David Engel --- .../SqlBulkCopyColumnOrderHintCollection.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index e644b7d22b..5027c93fc9 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -9,8 +9,7 @@ Column order hints define the sort order of the column in the destination table. SqlBulkCopy's performance is improved if the data being imported is sorted according to the clustered index on the table, if any. -If the data is sorted in a different order, that is other than the order of a clustered index key or if there is no clustered index on the table, -the order hint is ignored. +If the data is sorted in an order that differs from the order of a clustered index key or if there is no clustered index on the table, the order hint is ignored. Order hints can be specified for any number of columns in the destination table. By default, the bulk insert operation assumes the data is unordered if no hints are provided. From 968bd262a8f1686f4eb0c1714866829bf807bfec Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Tue, 19 May 2020 12:20:23 -0700 Subject: [PATCH 17/34] Addressing review comments --- src/Microsoft.Data.SqlClient.sln | 2 +- .../netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index 2d9639f8b5..f4b4585854 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -767,7 +767,7 @@ Global {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp2.1-Release|x86.Build.0 = netcoreapp2.1-Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.ActiveCfg = Debug|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|Any CPU.Build.0 = Debug|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.ActiveCfg = netcoreapp3.1-Debug|x64 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x64.Build.0 = netcoreapp3.1-Debug|x64 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.ActiveCfg = netcoreapp3.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index d4648f78f6..56946af08d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -603,7 +603,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i int nmatched = 0; // Number of columns that match and are accepted int nrejected = 0; // Number of columns that match but were rejected bool rejectColumn; // True if a column is rejected because of an excluded type - + bool isInTransaction; isInTransaction = _connection.HasLocalTransaction; @@ -1337,7 +1337,7 @@ private void CreateOrValidateConnection(string method) { throw ADP.ConnectionRequired(method); } - + if (_ownConnection && _connection.State != ConnectionState.Open) { _connection.Open(); From cb2eef3af05c494c23092ccff73315ea11ceafe0 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Tue, 19 May 2020 12:23:13 -0700 Subject: [PATCH 18/34] spacing --- src/Microsoft.Data.SqlClient.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index f4b4585854..6c9a8f59b8 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -773,7 +773,7 @@ Global {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Debug|x86.Build.0 = netcoreapp3.1-Debug|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.ActiveCfg = Release|Any CPU {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|Any CPU.Build.0 = Release|Any CPU - {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 + {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.ActiveCfg = netcoreapp3.1-Release|x64 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x64.Build.0 = netcoreapp3.1-Release|x64 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.ActiveCfg = netcoreapp3.1-Release|x86 {9073ABEF-92E0-4702-BB23-2C99CEF9BDD7}.netcoreapp3.1-Release|x86.Build.0 = netcoreapp3.1-Release|x86 From 2afb1a89537dfa9711ee7549985522683fc4ff21 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Tue, 19 May 2020 16:20:11 -0700 Subject: [PATCH 19/34] Added test for consecutive WriteToServer calls --- .../SQL/SqlBulkCopyTest/OrderHint.cs | 34 ++++++++++++++--- .../SQL/SqlBulkCopyTest/OrderHintAsync.cs | 37 +++++++++++++++---- .../SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs | 14 +++---- 3 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs index ff3573d529..7145679431 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHint.cs @@ -5,29 +5,37 @@ using System; using System.Data; using System.Data.Common; +using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class OrderHint { private static readonly string sourceTable = "Customers"; + private static readonly string sourceTable2 = "Employees"; private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50)); CREATE CLUSTERED INDEX IX_Test_Table_Customer_ID ON {0} (CustomerID DESC)"; - private static readonly string sourceQueryTemplate = "SELECT CustomerId, CompanyName, ContactName FROM {0}"; + private static readonly string initialQueryTemplate2 = "create table {0} (LastName nvarchar(50), FirstName nvarchar(50))"; + private static readonly string sourceQueryTemplate = "SELECT CustomerID, CompanyName, ContactName FROM {0}"; + private static readonly string sourceQueryTemplate2 = "SELECT LastName, FirstName FROM {0}"; private static readonly string getRowCountQueryTemplate = "SELECT COUNT(*) FROM {0}"; - public static void Test(string srcConstr, string dstTable) + public static void Test(string srcConstr, string dstTable, string dstTable2) { srcConstr = (new SqlConnectionStringBuilder(srcConstr) { InitialCatalog = "Northwind" }).ConnectionString; string dstConstr = (new SqlConnectionStringBuilder(srcConstr)).ConnectionString; string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); + string sourceQuery2 = string.Format(sourceQueryTemplate2, sourceTable2); string initialQuery = string.Format(initialQueryTemplate, dstTable); + string initialQuery2 = string.Format(initialQueryTemplate2, dstTable2); string getRowCountQuery = string.Format(getRowCountQueryTemplate, sourceTable); - + string getRowCountQuery2 = string.Format(getRowCountQueryTemplate, sourceTable2); + using (SqlConnection dstConn = new SqlConnection(dstConstr)) using (SqlCommand dstCmd = dstConn.CreateCommand()) { dstConn.Open(); Helpers.TryExecute(dstCmd, initialQuery); + Helpers.TryExecute(dstCmd, initialQuery2); using (SqlConnection srcConn = new SqlConnection(srcConstr)) using (SqlCommand srcCmd = new SqlCommand(getRowCountQuery, srcConn)) { @@ -35,6 +43,8 @@ public static void Test(string srcConstr, string dstTable) try { int nRowsInSource = Convert.ToInt32(srcCmd.ExecuteScalar()); + srcCmd.CommandText = getRowCountQuery2; + int nRowsInSource2 = Convert.ToInt32(srcCmd.ExecuteScalar()); srcCmd.CommandText = sourceQuery; using (SqlBulkCopy bulkCopy = new SqlBulkCopy(dstConn)) { @@ -112,14 +122,26 @@ public static void Test(string srcConstr, string dstTable) { bulkcopy.WriteToServer(reader); } - } - const int nWriteToServerCalls = 8; - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * nWriteToServerCalls); + const int nWriteToServerCalls = 8; + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * nWriteToServerCalls); + + // different tables + srcCmd.CommandText = sourceQuery2; + using (DbDataReader reader = srcCmd.ExecuteReader()) + { + bulkcopy.DestinationTableName = dstTable2; + bulkcopy.ColumnOrderHints.Clear(); + bulkcopy.ColumnOrderHints.Add("LastName", SortOrder.Descending); + bulkcopy.WriteToServer(reader); + Helpers.VerifyResults(dstConn, dstTable2, 2, nRowsInSource2); + } + } } finally { DataTestUtility.DropTable(dstConn, dstTable); + DataTestUtility.DropTable(dstConn, dstTable2); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs index d477828029..d9c4515428 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/OrderHintAsync.cs @@ -13,30 +13,37 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests public class OrderHintAsync { private static readonly string sourceTable = "Customers"; - private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50))"; - private static readonly string sourceQueryTemplate = "SELECT CustomerId, CompanyName, ContactName FROM {0}"; + private static readonly string sourceTable2 = "Employees"; + private static readonly string initialQueryTemplate = "create table {0} (CustomerID nvarchar(50), CompanyName nvarchar(50), ContactName nvarchar(50)); CREATE CLUSTERED INDEX IX_Test_Table_Customer_ID ON {0} (CustomerID DESC)"; + private static readonly string initialQueryTemplate2 = "create table {0} (LastName nvarchar(50), FirstName nvarchar(50))"; + private static readonly string sourceQueryTemplate = "SELECT CustomerID, CompanyName, ContactName FROM {0}"; + private static readonly string sourceQueryTemplate2 = "SELECT LastName, FirstName FROM {0}"; private static readonly string getRowCountQueryTemplate = "SELECT COUNT(*) FROM {0}"; - public static void Test(string srcConstr, string dstTable) + public static void Test(string srcConstr, string dstTable, string dstTable2) { - Task t = TestAsync(srcConstr, dstTable); + Task t = TestAsync(srcConstr, dstTable, dstTable2); t.Wait(); Assert.True(t.IsCompleted, "Task did not complete! Status: " + t.Status); } - public static async Task TestAsync(string srcConstr, string dstTable) + public static async Task TestAsync(string srcConstr, string dstTable, string dstTable2) { srcConstr = (new SqlConnectionStringBuilder(srcConstr) { InitialCatalog = "Northwind" }).ConnectionString; string dstConstr = (new SqlConnectionStringBuilder(srcConstr)).ConnectionString; string sourceQuery = string.Format(sourceQueryTemplate, sourceTable); + string sourceQuery2 = string.Format(sourceQueryTemplate2, sourceTable2); string initialQuery = string.Format(initialQueryTemplate, dstTable); + string initialQuery2 = string.Format(initialQueryTemplate2, dstTable2); string getRowCountQuery = string.Format(getRowCountQueryTemplate, sourceTable); + string getRowCountQuery2 = string.Format(getRowCountQueryTemplate, sourceTable2); using (SqlConnection dstConn = new SqlConnection(dstConstr)) using (SqlCommand dstCmd = dstConn.CreateCommand()) { await dstConn.OpenAsync(); Helpers.TryExecute(dstCmd, initialQuery); + Helpers.TryExecute(dstCmd, initialQuery2); using (SqlConnection srcConn = new SqlConnection(srcConstr)) using (SqlCommand srcCmd = new SqlCommand(getRowCountQuery, srcConn)) { @@ -44,6 +51,8 @@ public static async Task TestAsync(string srcConstr, string dstTable) try { int nRowsInSource = Convert.ToInt32(await srcCmd.ExecuteScalarAsync()); + srcCmd.CommandText = getRowCountQuery2; + int nRowsInSource2 = Convert.ToInt32(await srcCmd.ExecuteScalarAsync()); srcCmd.CommandText = sourceQuery; using (SqlBulkCopy bulkCopy = new SqlBulkCopy(dstConn)) { @@ -95,14 +104,26 @@ public static async Task TestAsync(string srcConstr, string dstTable) { await bulkcopy.WriteToServerAsync(reader); } - } - const int nWriteToServerCalls = 5; - Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * nWriteToServerCalls); + const int nWriteToServerCalls = 5; + Helpers.VerifyResults(dstConn, dstTable, 3, nRowsInSource * nWriteToServerCalls); + + // different tables + srcCmd.CommandText = sourceQuery2; + using (DbDataReader reader = await srcCmd.ExecuteReaderAsync()) + { + bulkcopy.DestinationTableName = dstTable2; + bulkcopy.ColumnOrderHints.Clear(); + bulkcopy.ColumnOrderHints.Add("LastName", SortOrder.Descending); + await bulkcopy.WriteToServerAsync(reader); + Helpers.VerifyResults(dstConn, dstTable2, 2, nRowsInSource2); + } + } } finally { DataTestUtility.DropTable(dstConn, dstTable); + DataTestUtility.DropTable(dstConn, dstTable2); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs index 77c7ff75ba..dc789e3ff4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/SqlBulkCopyTest.cs @@ -251,7 +251,13 @@ public void DestinationTableNameWithSpecialCharTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public void OrderHintTest() { - OrderHint.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHint")); + OrderHint.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHint"), AddGuid("SqlBulkCopyTest_OrderHint2")); + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public void OrderHintAsyncTest() + { + OrderHintAsync.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHintAsync"), AddGuid("SqlBulkCopyTest_OrderHintAsync2")); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] @@ -272,12 +278,6 @@ public void OrderHintTransactionTest() OrderHintTransaction.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHintTransaction")); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void OrderHintAsyncTest() - { - OrderHintAsync.Test(srcConstr, AddGuid("SqlBulkCopyTest_OrderHintAsync")); - } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] [ActiveIssue(12219)] public void OrderHintIdentityColumnTest() From 9ed653de8a449f800848bdfa3ac7b7e3a17ba7e6 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Wed, 20 May 2020 14:53:22 -0700 Subject: [PATCH 20/34] Fixed test --- .../SqlBulkCopyColumnOrderHintCollectionTest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index eec43b16f3..0e165be5ac 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -560,7 +560,7 @@ public void Add_DuplicateColumnNames_NotAllowed() private void ValidateCollection(SqlBulkCopyColumnOrderHintCollection collection, int expectedCount) { Assert.True(expectedCount == collection.Count, "Collection was not the expected size."); - bool valid; + bool valid = true; HashSet columnNames = new HashSet(); foreach (SqlBulkCopyColumnOrderHint orderHint in collection) { @@ -573,7 +573,6 @@ private void ValidateCollection(SqlBulkCopyColumnOrderHintCollection collection, valid = false; } } - valid = true; Assert.True(valid, "Collection contained a duplicate column name."); } } From 985662fdd790e19581c6f03afdd37ab613ead5f1 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Thu, 21 May 2020 21:01:52 -0700 Subject: [PATCH 21/34] Update Microsoft.Data.SqlClient.ManualTesting.Tests.csproj --- .../Microsoft.Data.SqlClient.ManualTesting.Tests.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index bbb03030f0..d0152fca05 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -61,7 +61,6 @@ Common\System\Collections\DictionaryExtensions.cs - From 477913ccfe3dbb4f5e0d31330f6ae67d5ff76343 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Thu, 21 May 2020 21:12:36 -0700 Subject: [PATCH 22/34] Update SqlBulkCopy_ColumnOrderHintCollectionClear.cs --- doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs index 066ecf5c2c..0df1281b52 100644 --- a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs @@ -1,6 +1,6 @@ +// using System; using System.Data; -// using Microsoft.Data.SqlClient; class Program From 5d9b9f5eef94c1cd87e788364fe345cfbc0df6c3 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Thu, 21 May 2020 21:27:42 -0700 Subject: [PATCH 23/34] Update SqlBulkCopyColumnOrderHint.xml --- .../Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml index 93e979bc25..b6b1a363d9 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml @@ -83,8 +83,6 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. An will be thrown if a null or empty string is given. -The last value set takes precedence. - ## Examples The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. @@ -114,8 +112,6 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. ## Remarks An will be thrown if a of Unspecified is given. -The last value set takes precedence. - ## Examples The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. From d5fe90d082a9e0de420dafa735cc51c174656aa0 Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Thu, 21 May 2020 21:34:12 -0700 Subject: [PATCH 24/34] Update SqlBulkCopyColumnOrderHintCollection.xml --- .../SqlBulkCopyColumnOrderHintCollection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index 5027c93fc9..f2114b7a3a 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -74,7 +74,7 @@ it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to c ## Examples The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. -A SqlBulkCopyColumnOrderHint object is added to the by providing the destination column name and its sort order. +A SqlBulkCopyColumnOrderHint object is added to the by providing the destination column name and its sort order. > [!IMPORTANT] > This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). From 9cde0c4ec357c59d05da0c366a4342b135c210ed Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Thu, 28 May 2020 17:26:00 -0700 Subject: [PATCH 25/34] Addressing comments --- .../SqlBulkCopyColumnOrderHintCollection.xml | 4 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 -- .../netcore/src/Resources/SR.Designer.cs | 9 ----- .../netcore/src/Resources/SR.resx | 5 +-- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 -- .../netfx/src/Resources/Strings.Designer.cs | 9 ----- .../netfx/src/Resources/Strings.resx | 5 +-- .../SqlBulkCopyColumnOrderHintCollection.cs | 40 ++++++------------- ...qlBulkCopyColumnOrderHintCollectionTest.cs | 1 - 9 files changed, 16 insertions(+), 65 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index 5027c93fc9..e553cb6c75 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -138,7 +138,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. Integer value of the location within the at which to insert the new . - + object to be inserted in the collection. Insert a new at the index specified. The order in which column order hints can be added is arbitrary. @@ -150,7 +150,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. To be added. - + object to be removed from the collection. Removes the specified element from the . diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index 5e1cdf3df0..9bd5aa25f5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -852,10 +852,6 @@ internal static Exception BulkLoadInvalidOrderHint() { return ADP.Argument(System.SRHelper.GetString(SR.SQL_BulkLoadInvalidOrderHint)); } - internal static Exception BulkLoadOrderHintInaccessible() - { - return ADP.InvalidOperation(System.SRHelper.GetString(SR.SQL_BulkLoadOrderHintInaccessible)); - } internal static Exception BulkLoadOrderHintInvalidColumn(string columnName) { return ADP.InvalidOperation(string.Format(System.SRHelper.GetString(SR.SQL_BulkLoadOrderHintInvalidColumn), columnName)); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index 0288addf8d..9923853c98 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -2301,15 +2301,6 @@ internal static string SQL_BulkLoadOrderHintDuplicateColumn { } } - /// - /// Looks up a localized string similar to The order hint collection is in use and cannot be accessed at this time.. - /// - internal static string SQL_BulkLoadOrderHintInaccessible { - get { - return ResourceManager.GetString("SQL_BulkLoadOrderHintInaccessible", resourceCulture); - } - } - /// /// Looks up a localized string similar to The sorted column '{0}' is not valid in the destination table.. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index 992c60ae79..3bbfa99364 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1866,9 +1866,6 @@ A column order hint cannot have an unspecified sort order. - - The order hint collection is in use and cannot be accessed at this time. - The given column order hint is not valid. @@ -1878,4 +1875,4 @@ The sorted column '{0}' is not valid in the destination table. - + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index 247a7d68b8..40c6765d32 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1005,10 +1005,6 @@ internal static Exception BulkLoadInvalidOrderHint() { return ADP.Argument(StringsHelper.GetString(Strings.SQL_BulkLoadInvalidOrderHint)); } - internal static Exception BulkLoadOrderHintInaccessible() - { - return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInaccessible)); - } internal static Exception BulkLoadOrderHintInvalidColumn(string columnName) { return ADP.InvalidOperation(string.Format(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInvalidColumn), columnName)); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 0c23ebc7a0..d8f48e601e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -9018,15 +9018,6 @@ internal static string SQL_BulkLoadOrderHintDuplicateColumn { } } - /// - /// Looks up a localized string similar to The order hint collection is in use and cannot be accessed at this time.. - /// - internal static string SQL_BulkLoadOrderHintInaccessible { - get { - return ResourceManager.GetString("SQL_BulkLoadOrderHintInaccessible", resourceCulture); - } - } - /// /// Looks up a localized string similar to The sorted column '{0}' is not valid in the destination table.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 96b03a0cb0..eaacefafd0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4536,13 +4536,10 @@ The column '{0}' was specified more than once. - - The order hint collection is in use and cannot be accessed at this time. - The sorted column '{0}' is not valid in the destination table. A column order hint cannot have an unspecified sort order. - + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs index 094b28360a..e5536ffbd5 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs @@ -11,8 +11,6 @@ namespace Microsoft.Data.SqlClient /// public sealed class SqlBulkCopyColumnOrderHintCollection : CollectionBase { - internal bool ReadOnly { get; set; } - private readonly HashSet _columnNames = new HashSet(); /// @@ -21,10 +19,9 @@ public sealed class SqlBulkCopyColumnOrderHintCollection : CollectionBase /// public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint) { - AssertWriteAccess(); if (columnOrderHint == null) { - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(columnOrderHint)); } if (string.IsNullOrEmpty(columnOrderHint.Column) || columnOrderHint.SortOrder == SortOrder.Unspecified) @@ -39,22 +36,12 @@ public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint /// public SqlBulkCopyColumnOrderHint Add(string column, SortOrder sortOrder) { - AssertWriteAccess(); return Add(new SqlBulkCopyColumnOrderHint(column, sortOrder)); } - private void AssertWriteAccess() - { - if (ReadOnly) - { - throw SQL.BulkLoadOrderHintInaccessible(); - } - } - /// public new void Clear() { - AssertWriteAccess(); foreach (SqlBulkCopyColumnOrderHint orderHint in InnerList) { UnregisterColumnName(orderHint, orderHint.Column); @@ -72,38 +59,35 @@ private void AssertWriteAccess() public int IndexOf(SqlBulkCopyColumnOrderHint value) => InnerList.IndexOf(value); /// - public void Insert(int index, SqlBulkCopyColumnOrderHint value) + public void Insert(int index, SqlBulkCopyColumnOrderHint columnOrderHint) { - AssertWriteAccess(); // Try inserting into an invalid index to throw an exception if (index < 0 || index > InnerList.Count) { - InnerList.Insert(index, value); + InnerList.Insert(index, columnOrderHint); } - if (value == null) + if (columnOrderHint == null) { - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(columnOrderHint)); } - RegisterColumnName(value, value.Column); - InnerList.Insert(index, value); + RegisterColumnName(columnOrderHint, columnOrderHint.Column); + InnerList.Insert(index, columnOrderHint); } /// - public void Remove(SqlBulkCopyColumnOrderHint value) + public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) { - AssertWriteAccess(); - if (value == null) + if (columnOrderHint == null) { - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(columnOrderHint)); } - UnregisterColumnName(value, value.Column); - InnerList.Remove(value); + UnregisterColumnName(columnOrderHint, columnOrderHint.Column); + InnerList.Remove(columnOrderHint); } /// public new void RemoveAt(int index) { - AssertWriteAccess(); var orderHint = (SqlBulkCopyColumnOrderHint)InnerList[index]; UnregisterColumnName(orderHint, orderHint.Column); base.RemoveAt(index); diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index 0e165be5ac..cd0ec3a885 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -23,7 +23,6 @@ private static SqlBulkCopyColumnOrderHintCollection CreateCollection(params SqlB foreach (SqlBulkCopyColumnOrderHint orderHint in orderHints) { - Debug.Assert(orderHints != null); collection.Add(orderHint); } From 2140fd7309a8ddf308e13ce1783b61c546c76810 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Thu, 28 May 2020 17:54:33 -0700 Subject: [PATCH 26/34] Revert "Addressing comments" This reverts commit 9cde0c4ec357c59d05da0c366a4342b135c210ed. --- .../SqlBulkCopyColumnOrderHintCollection.xml | 4 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 ++ .../netcore/src/Resources/SR.Designer.cs | 9 +++++ .../netcore/src/Resources/SR.resx | 5 ++- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 ++ .../netfx/src/Resources/Strings.Designer.cs | 9 +++++ .../netfx/src/Resources/Strings.resx | 5 ++- .../SqlBulkCopyColumnOrderHintCollection.cs | 40 +++++++++++++------ ...qlBulkCopyColumnOrderHintCollectionTest.cs | 1 + 9 files changed, 65 insertions(+), 16 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index 39a1ad67c5..f2114b7a3a 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -138,7 +138,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. Integer value of the location within the at which to insert the new . - + object to be inserted in the collection. Insert a new at the index specified. The order in which column order hints can be added is arbitrary. @@ -150,7 +150,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. To be added. - + object to be removed from the collection. Removes the specified element from the . diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index 9bd5aa25f5..5e1cdf3df0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -852,6 +852,10 @@ internal static Exception BulkLoadInvalidOrderHint() { return ADP.Argument(System.SRHelper.GetString(SR.SQL_BulkLoadInvalidOrderHint)); } + internal static Exception BulkLoadOrderHintInaccessible() + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.SQL_BulkLoadOrderHintInaccessible)); + } internal static Exception BulkLoadOrderHintInvalidColumn(string columnName) { return ADP.InvalidOperation(string.Format(System.SRHelper.GetString(SR.SQL_BulkLoadOrderHintInvalidColumn), columnName)); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index 9923853c98..0288addf8d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -2301,6 +2301,15 @@ internal static string SQL_BulkLoadOrderHintDuplicateColumn { } } + /// + /// Looks up a localized string similar to The order hint collection is in use and cannot be accessed at this time.. + /// + internal static string SQL_BulkLoadOrderHintInaccessible { + get { + return ResourceManager.GetString("SQL_BulkLoadOrderHintInaccessible", resourceCulture); + } + } + /// /// Looks up a localized string similar to The sorted column '{0}' is not valid in the destination table.. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index 3bbfa99364..992c60ae79 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1866,6 +1866,9 @@ A column order hint cannot have an unspecified sort order. + + The order hint collection is in use and cannot be accessed at this time. + The given column order hint is not valid. @@ -1875,4 +1878,4 @@ The sorted column '{0}' is not valid in the destination table. - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index 40c6765d32..247a7d68b8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1005,6 +1005,10 @@ internal static Exception BulkLoadInvalidOrderHint() { return ADP.Argument(StringsHelper.GetString(Strings.SQL_BulkLoadInvalidOrderHint)); } + internal static Exception BulkLoadOrderHintInaccessible() + { + return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInaccessible)); + } internal static Exception BulkLoadOrderHintInvalidColumn(string columnName) { return ADP.InvalidOperation(string.Format(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInvalidColumn), columnName)); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index d8f48e601e..0c23ebc7a0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -9018,6 +9018,15 @@ internal static string SQL_BulkLoadOrderHintDuplicateColumn { } } + /// + /// Looks up a localized string similar to The order hint collection is in use and cannot be accessed at this time.. + /// + internal static string SQL_BulkLoadOrderHintInaccessible { + get { + return ResourceManager.GetString("SQL_BulkLoadOrderHintInaccessible", resourceCulture); + } + } + /// /// Looks up a localized string similar to The sorted column '{0}' is not valid in the destination table.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index eaacefafd0..96b03a0cb0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4536,10 +4536,13 @@ The column '{0}' was specified more than once. + + The order hint collection is in use and cannot be accessed at this time. + The sorted column '{0}' is not valid in the destination table. A column order hint cannot have an unspecified sort order. - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs index e5536ffbd5..094b28360a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs @@ -11,6 +11,8 @@ namespace Microsoft.Data.SqlClient /// public sealed class SqlBulkCopyColumnOrderHintCollection : CollectionBase { + internal bool ReadOnly { get; set; } + private readonly HashSet _columnNames = new HashSet(); /// @@ -19,9 +21,10 @@ public sealed class SqlBulkCopyColumnOrderHintCollection : CollectionBase /// public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint) { + AssertWriteAccess(); if (columnOrderHint == null) { - throw new ArgumentNullException(nameof(columnOrderHint)); + throw new ArgumentNullException(); } if (string.IsNullOrEmpty(columnOrderHint.Column) || columnOrderHint.SortOrder == SortOrder.Unspecified) @@ -36,12 +39,22 @@ public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint /// public SqlBulkCopyColumnOrderHint Add(string column, SortOrder sortOrder) { + AssertWriteAccess(); return Add(new SqlBulkCopyColumnOrderHint(column, sortOrder)); } + private void AssertWriteAccess() + { + if (ReadOnly) + { + throw SQL.BulkLoadOrderHintInaccessible(); + } + } + /// public new void Clear() { + AssertWriteAccess(); foreach (SqlBulkCopyColumnOrderHint orderHint in InnerList) { UnregisterColumnName(orderHint, orderHint.Column); @@ -59,35 +72,38 @@ public SqlBulkCopyColumnOrderHint Add(string column, SortOrder sortOrder) public int IndexOf(SqlBulkCopyColumnOrderHint value) => InnerList.IndexOf(value); /// - public void Insert(int index, SqlBulkCopyColumnOrderHint columnOrderHint) + public void Insert(int index, SqlBulkCopyColumnOrderHint value) { + AssertWriteAccess(); // Try inserting into an invalid index to throw an exception if (index < 0 || index > InnerList.Count) { - InnerList.Insert(index, columnOrderHint); + InnerList.Insert(index, value); } - if (columnOrderHint == null) + if (value == null) { - throw new ArgumentNullException(nameof(columnOrderHint)); + throw new ArgumentNullException(); } - RegisterColumnName(columnOrderHint, columnOrderHint.Column); - InnerList.Insert(index, columnOrderHint); + RegisterColumnName(value, value.Column); + InnerList.Insert(index, value); } /// - public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) + public void Remove(SqlBulkCopyColumnOrderHint value) { - if (columnOrderHint == null) + AssertWriteAccess(); + if (value == null) { - throw new ArgumentNullException(nameof(columnOrderHint)); + throw new ArgumentNullException(); } - UnregisterColumnName(columnOrderHint, columnOrderHint.Column); - InnerList.Remove(columnOrderHint); + UnregisterColumnName(value, value.Column); + InnerList.Remove(value); } /// public new void RemoveAt(int index) { + AssertWriteAccess(); var orderHint = (SqlBulkCopyColumnOrderHint)InnerList[index]; UnregisterColumnName(orderHint, orderHint.Column); base.RemoveAt(index); diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index cd0ec3a885..0e165be5ac 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -23,6 +23,7 @@ private static SqlBulkCopyColumnOrderHintCollection CreateCollection(params SqlB foreach (SqlBulkCopyColumnOrderHint orderHint in orderHints) { + Debug.Assert(orderHints != null); collection.Add(orderHint); } From 465fa7439fcd1e0c109368e16f96ba4a6ff91ec3 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Thu, 28 May 2020 17:54:41 -0700 Subject: [PATCH 27/34] Revert "Merge branch 'bulkCopyOrderHints' of https://github.com/johnnypham/SqlClient into bulkCopyOrderHints" This reverts commit 0093a06fbb684fc1e2055f9f0f219d68f36b1596, reversing changes made to 9cde0c4ec357c59d05da0c366a4342b135c210ed. --- doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs | 2 +- .../Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml | 4 ++++ .../SqlBulkCopyColumnOrderHintCollection.xml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs index 0df1281b52..066ecf5c2c 100644 --- a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs @@ -1,6 +1,6 @@ -// using System; using System.Data; +// using Microsoft.Data.SqlClient; class Program diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml index b6b1a363d9..93e979bc25 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml @@ -83,6 +83,8 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. An will be thrown if a null or empty string is given. +The last value set takes precedence. + ## Examples The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. @@ -112,6 +114,8 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. ## Remarks An will be thrown if a of Unspecified is given. +The last value set takes precedence. + ## Examples The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index f2114b7a3a..5027c93fc9 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -74,7 +74,7 @@ it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to c ## Examples The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. -A SqlBulkCopyColumnOrderHint object is added to the by providing the destination column name and its sort order. +A SqlBulkCopyColumnOrderHint object is added to the by providing the destination column name and its sort order. > [!IMPORTANT] > This sample will not run unless you have created the work tables as described in [Bulk Copy Example Setup](~/docs/framework/data/adonet/sql/bulk-copy-example-setup.md). From ecb43a794057e48bc0066ad6c256b8878e0d2b54 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Fri, 29 May 2020 13:05:58 -0700 Subject: [PATCH 28/34] Addressing comments --- ...BulkCopy_ColumnOrderHintCollectionClear.cs | 2 +- .../SqlBulkCopyColumnOrderHint.xml | 4 -- .../SqlBulkCopyColumnOrderHintCollection.xml | 4 +- .../netcore/ref/Microsoft.Data.SqlClient.cs | 4 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 -- .../netcore/src/Resources/SR.Designer.cs | 9 ---- .../netcore/src/Resources/SR.resx | 3 -- .../netfx/ref/Microsoft.Data.SqlClient.cs | 4 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 -- .../netfx/src/Resources/Strings.Designer.cs | 9 ---- .../netfx/src/Resources/Strings.resx | 3 -- .../SqlBulkCopyColumnOrderHintCollection.cs | 45 ++++++------------- ...qlBulkCopyColumnOrderHintCollectionTest.cs | 2 - 13 files changed, 20 insertions(+), 77 deletions(-) diff --git a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs index 066ecf5c2c..0df1281b52 100644 --- a/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs +++ b/doc/samples/SqlBulkCopy_ColumnOrderHintCollectionClear.cs @@ -1,6 +1,6 @@ +// using System; using System.Data; -// using Microsoft.Data.SqlClient; class Program diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml index 93e979bc25..b6b1a363d9 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml @@ -83,8 +83,6 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. An will be thrown if a null or empty string is given. -The last value set takes precedence. - ## Examples The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. @@ -114,8 +112,6 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. ## Remarks An will be thrown if a of Unspecified is given. -The last value set takes precedence. - ## Examples The following example bulk copies data from a source table in the **AdventureWorks** sample database to a destination table in the same database. A SqlBulkCopyColumnOrderHint object is used to define the sort order for the ProductNumber destination column. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index 5027c93fc9..e553cb6c75 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -138,7 +138,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. Integer value of the location within the at which to insert the new . - + object to be inserted in the collection. Insert a new at the index specified. The order in which column order hints can be added is arbitrary. @@ -150,7 +150,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. To be added. - + object to be removed from the collection. Removes the specified element from the . diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 470010ad28..fdca2a2ebf 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -267,9 +267,9 @@ public void CopyTo(SqlBulkCopyColumnOrderHint[] array, int index) { } /// public int IndexOf(SqlBulkCopyColumnOrderHint value) { throw null; } /// - public void Insert(int index, SqlBulkCopyColumnOrderHint value) { } + public void Insert(int index, SqlBulkCopyColumnOrderHint columnOrderHint) { } /// - public void Remove(SqlBulkCopyColumnOrderHint value) { } + public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) { } /// public new void RemoveAt(int index) { } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index 5e1cdf3df0..9bd5aa25f5 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -852,10 +852,6 @@ internal static Exception BulkLoadInvalidOrderHint() { return ADP.Argument(System.SRHelper.GetString(SR.SQL_BulkLoadInvalidOrderHint)); } - internal static Exception BulkLoadOrderHintInaccessible() - { - return ADP.InvalidOperation(System.SRHelper.GetString(SR.SQL_BulkLoadOrderHintInaccessible)); - } internal static Exception BulkLoadOrderHintInvalidColumn(string columnName) { return ADP.InvalidOperation(string.Format(System.SRHelper.GetString(SR.SQL_BulkLoadOrderHintInvalidColumn), columnName)); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index 0288addf8d..9923853c98 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -2301,15 +2301,6 @@ internal static string SQL_BulkLoadOrderHintDuplicateColumn { } } - /// - /// Looks up a localized string similar to The order hint collection is in use and cannot be accessed at this time.. - /// - internal static string SQL_BulkLoadOrderHintInaccessible { - get { - return ResourceManager.GetString("SQL_BulkLoadOrderHintInaccessible", resourceCulture); - } - } - /// /// Looks up a localized string similar to The sorted column '{0}' is not valid in the destination table.. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index 992c60ae79..667d9890d0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1866,9 +1866,6 @@ A column order hint cannot have an unspecified sort order. - - The order hint collection is in use and cannot be accessed at this time. - The given column order hint is not valid. diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index afa88b00eb..6cad12f494 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -283,9 +283,9 @@ public void CopyTo(SqlBulkCopyColumnOrderHint[] array, int index) { } /// public int IndexOf(SqlBulkCopyColumnOrderHint value) { throw null; } /// - public void Insert(int index, SqlBulkCopyColumnOrderHint value) { } + public void Insert(int index, SqlBulkCopyColumnOrderHint columnOrderHint) { } /// - public void Remove(SqlBulkCopyColumnOrderHint value) { } + public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) { } /// public new void RemoveAt(int index) { } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index 247a7d68b8..40c6765d32 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1005,10 +1005,6 @@ internal static Exception BulkLoadInvalidOrderHint() { return ADP.Argument(StringsHelper.GetString(Strings.SQL_BulkLoadInvalidOrderHint)); } - internal static Exception BulkLoadOrderHintInaccessible() - { - return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInaccessible)); - } internal static Exception BulkLoadOrderHintInvalidColumn(string columnName) { return ADP.InvalidOperation(string.Format(StringsHelper.GetString(Strings.SQL_BulkLoadOrderHintInvalidColumn), columnName)); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 0c23ebc7a0..d8f48e601e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -9018,15 +9018,6 @@ internal static string SQL_BulkLoadOrderHintDuplicateColumn { } } - /// - /// Looks up a localized string similar to The order hint collection is in use and cannot be accessed at this time.. - /// - internal static string SQL_BulkLoadOrderHintInaccessible { - get { - return ResourceManager.GetString("SQL_BulkLoadOrderHintInaccessible", resourceCulture); - } - } - /// /// Looks up a localized string similar to The sorted column '{0}' is not valid in the destination table.. /// diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 96b03a0cb0..70731abfab 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4536,9 +4536,6 @@ The column '{0}' was specified more than once. - - The order hint collection is in use and cannot be accessed at this time. - The sorted column '{0}' is not valid in the destination table. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs index 094b28360a..9fbb131baf 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs @@ -11,8 +11,6 @@ namespace Microsoft.Data.SqlClient /// public sealed class SqlBulkCopyColumnOrderHintCollection : CollectionBase { - internal bool ReadOnly { get; set; } - private readonly HashSet _columnNames = new HashSet(); /// @@ -21,10 +19,9 @@ public sealed class SqlBulkCopyColumnOrderHintCollection : CollectionBase /// public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint) { - AssertWriteAccess(); if (columnOrderHint == null) { - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(columnOrderHint)); } if (string.IsNullOrEmpty(columnOrderHint.Column) || columnOrderHint.SortOrder == SortOrder.Unspecified) @@ -37,24 +34,11 @@ public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint } /// - public SqlBulkCopyColumnOrderHint Add(string column, SortOrder sortOrder) - { - AssertWriteAccess(); - return Add(new SqlBulkCopyColumnOrderHint(column, sortOrder)); - } - - private void AssertWriteAccess() - { - if (ReadOnly) - { - throw SQL.BulkLoadOrderHintInaccessible(); - } - } + public SqlBulkCopyColumnOrderHint Add(string column, SortOrder sortOrder) => Add(new SqlBulkCopyColumnOrderHint(column, sortOrder)); /// public new void Clear() { - AssertWriteAccess(); foreach (SqlBulkCopyColumnOrderHint orderHint in InnerList) { UnregisterColumnName(orderHint, orderHint.Column); @@ -72,38 +56,35 @@ private void AssertWriteAccess() public int IndexOf(SqlBulkCopyColumnOrderHint value) => InnerList.IndexOf(value); /// - public void Insert(int index, SqlBulkCopyColumnOrderHint value) + public void Insert(int index, SqlBulkCopyColumnOrderHint columnOrderHint) { - AssertWriteAccess(); // Try inserting into an invalid index to throw an exception if (index < 0 || index > InnerList.Count) { - InnerList.Insert(index, value); + InnerList.Insert(index, columnOrderHint); } - if (value == null) + if (columnOrderHint == null) { - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(columnOrderHint)); } - RegisterColumnName(value, value.Column); - InnerList.Insert(index, value); + RegisterColumnName(columnOrderHint, columnOrderHint.Column); + InnerList.Insert(index, columnOrderHint); } /// - public void Remove(SqlBulkCopyColumnOrderHint value) + public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) { - AssertWriteAccess(); - if (value == null) + if (columnOrderHint == null) { - throw new ArgumentNullException(); + throw new ArgumentNullException(nameof(columnOrderHint)); } - UnregisterColumnName(value, value.Column); - InnerList.Remove(value); + UnregisterColumnName(columnOrderHint, columnOrderHint.Column); + InnerList.Remove(columnOrderHint); } /// public new void RemoveAt(int index) { - AssertWriteAccess(); var orderHint = (SqlBulkCopyColumnOrderHint)InnerList[index]; UnregisterColumnName(orderHint, orderHint.Column); base.RemoveAt(index); diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index 0e165be5ac..fdd7eb56d9 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -6,7 +6,6 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using Xunit; namespace Microsoft.Data.SqlClient.Tests @@ -23,7 +22,6 @@ private static SqlBulkCopyColumnOrderHintCollection CreateCollection(params SqlB foreach (SqlBulkCopyColumnOrderHint orderHint in orderHints) { - Debug.Assert(orderHints != null); collection.Add(orderHint); } From 982a8026378bd949fdc66bafeeb1cfd9763e1977 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Thu, 4 Jun 2020 14:06:52 -0700 Subject: [PATCH 29/34] Addressing comments --- .../SqlBulkCopyColumnOrderHint.xml | 2 ++ .../SqlBulkCopyColumnOrderHintCollection.xml | 6 ++++++ .../Data/SqlClient/SqlBulkCopyColumnOrderHint.cs | 10 +++++----- .../SqlBulkCopyColumnOrderHintCollectionTest.cs | 10 +++++----- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml index b6b1a363d9..8a7b04cf68 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHint.xml @@ -95,6 +95,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.ColumnOrderHintColumn#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHintColumn.cs#1)] ]]> + The value is null or empty. @@ -124,6 +125,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.ColumnOrderHintSortOrder#1](~/../sqlclient/doc/samples/SqlBulkCopy_ColumnOrderHintSortOrder.cs#1)] ]]> + The sort order cannot be unspecified for a column order hint. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml index e553cb6c75..af8ba0984f 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopyColumnOrderHintCollection.xml @@ -60,6 +60,7 @@ it is easier and faster to use a Transact-SQL `INSERT … SELECT` statement to c ]]> + The value is null. @@ -142,12 +143,15 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. object to be inserted in the collection. Insert a new at the index specified. The order in which column order hints can be added is arbitrary. + The index is less than zero or greater than . + A null column order hint cannot be added to the collection. The zero-based index of the to find. Gets the object at the specified index. A object. To be added. + The index must be non-negative and less than the size of the collection. @@ -180,6 +184,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. ]]> + The value is null. The zero-based index of the object to be removed from the collection. @@ -211,6 +216,7 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. ]]> + The index must be non-negative and less than the size of the collection. Gets a value indicating whether access to the is synchronized (thread safe). diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs index 0f55b6f7b1..2329df4140 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHint.cs @@ -27,16 +27,16 @@ public string Column get => _columnName ?? string.Empty; set { + if (string.IsNullOrEmpty(value)) + { + throw SQL.BulkLoadNullEmptyColumnName(nameof(Column)); + } // Do nothing if column name is the same - if (!string.IsNullOrEmpty(value) && _columnName != value) + if (_columnName != value) { OnNameChanging(value); _columnName = value; } - else if (string.IsNullOrEmpty(value)) - { - throw SQL.BulkLoadNullEmptyColumnName(nameof(Column)); - } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index fdd7eb56d9..57bb81ba0c 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -16,7 +16,7 @@ public class SqlBulkCopyColumnOrderHintCollectionTest private static SqlBulkCopyColumnOrderHintCollection CreateCollection(params SqlBulkCopyColumnOrderHint[] orderHints) { - Debug.Assert(orderHints != null); + Assert.NotNull(orderHints); SqlBulkCopyColumnOrderHintCollection collection = CreateCollection(); @@ -140,10 +140,10 @@ public void IListAddInsert_InsertNonSqlBulkCopyColumnOrderHintItems_DoNotThrow() // The following operations should really throw ArgumentException due to the // mismatched types, but do not throw in the full framework. - string bogus = "Bogus"; - list[0] = bogus; - list.Add(bogus); - list.Insert(0, bogus); + string foo = "foo"; + list[0] = foo; + list.Add(foo); + list.Insert(0, foo); } [Fact] From d5b862020ce54b63868b93bedd27dd0c8de838da Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Thu, 4 Jun 2020 16:18:59 -0700 Subject: [PATCH 30/34] Addressing comments --- .../Microsoft.Data.SqlClient/SqlBulkCopy.xml | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml index 6209fbc6cc..ad780294cf 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml @@ -548,6 +548,11 @@ the object's `Finalize` method. To be added. + + A + + did not specify a valid destination column name. + @@ -590,6 +595,11 @@ Transact-SQL `INSERT … SELECT` statement to copy the data. [!code-csharp[SqlBulkCopy.ConnectionString#1](~/../sqlclient/doc/samples/SqlBulkCopy_ConnectionString.cs#1)] ]]> + + A + + did not specify a valid destination column name. + @@ -628,6 +638,11 @@ This code is provided to demonstrate the syntax for using **SqlBulkCopy** only. [!code-csharp[SqlBulkCopy.DataTable#1](~/../sqlclient/doc/samples/SqlBulkCopy_DataTable.cs#1)] ]]> + + A + + did not specify a valid destination column name. + Performing Bulk Copy Operations @@ -682,6 +697,11 @@ The method is calle [!code-csharp[SqlBulkCopy.DataRowState#1](~/../sqlclient/doc/samples/SqlBulkCopy_DataRowState.cs#1)] ]]> + + A + + did not specify a valid destination column name. + An array of objects that will be copied to the destination table. @@ -706,6 +726,11 @@ In this example, a is created at run time. A single ]]> + + A + + did not specify a valid destination column name. + @@ -758,6 +783,10 @@ For more information about asynchronous programming in the .NET Framework Data P object is closed before method execution. is specified in the connection string. + + A + + did not specify a valid destination column name. Returned in the task object, any error returned by SQL Server that occurred while opening the connection. @@ -815,6 +844,9 @@ For more information about asynchronous programming in the .NET Framework Data P object is closed before method execution. is specified in the connection string. + A + + did not specify a valid destination column name. Returned in the task object, any error returned by SQL Server that occurred while opening the connection. @@ -875,6 +907,9 @@ For more information about asynchronous programming in the .NET Framework Data P returned. is specified in the connection string. + A + + did not specify a valid destination column name. Returned in the task object, any error returned by SQL Server that occurred while opening the connection. @@ -978,6 +1013,10 @@ For more information about asynchronous programming in the .NET Framework Data P returned. is specified in the connection string. + + A + + did not specify a valid destination column name. Returned in the task object, any error returned by SQL Server that occurred while opening the connection. @@ -1055,6 +1094,10 @@ For more information about asynchronous programming in the .NET Framework Data P returned. is specified in the connection string. + + A + + did not specify a valid destination column name. Returned in the task object, any error returned by SQL Server that occurred while opening the connection. @@ -1112,8 +1155,12 @@ For more information about asynchronous programming in the .NET Framework Data P object is closed before method execution. is specified in the connection string. - - + + A + + did not specify a valid destination column name. + l + Returned in the task object, any error returned by SQL Server that occurred while opening the connection. @@ -1178,6 +1225,10 @@ For more information about asynchronous programming in the .NET Framework Data P object is closed before method execution. is specified in the connection string. + + A + + did not specify a valid destination column name. Returned in the task object, any error returned by SQL Server that occurred while opening the connection. @@ -1237,6 +1288,10 @@ For more information about asynchronous programming in the .NET Framework Data P object is closed before method execution. is specified in the connection string. + + A + + did not specify a valid destination column name. Returned in the task object, any error returned by SQL Server that occurred while opening the connection. @@ -1309,6 +1364,10 @@ For more information about asynchronous programming in the .NET Framework Data P object is closed before method execution. is specified in the connection string. + + A + + did not specify a valid destination column name. Returned in the task object, any error returned by SQL Server that occurred while opening the connection. From fd9183dd544d13f362389a5c7b784e2014c401bc Mon Sep 17 00:00:00 2001 From: Johnny Pham Date: Fri, 5 Jun 2020 08:30:35 -0700 Subject: [PATCH 31/34] Update SqlBulkCopy.xml --- doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml index ad780294cf..ab82b00a10 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlBulkCopy.xml @@ -1159,7 +1159,7 @@ For more information about asynchronous programming in the .NET Framework Data P A did not specify a valid destination column name. - l + Returned in the task object, any error returned by SQL Server that occurred while opening the connection. From d8a44b3b018ade9be471e044d427e3089e6a1211 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Tue, 9 Jun 2020 14:02:56 -0700 Subject: [PATCH 32/34] Addressing comments --- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 11 ++++---- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 11 ++++---- .../SqlBulkCopyColumnOrderHintCollection.cs | 26 ++++++++++++------- ...qlBulkCopyColumnOrderHintCollectionTest.cs | 20 +++++++++----- 4 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 56946af08d..adfc7ec37c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -233,7 +233,6 @@ private int RowNumber private TdsParser _parser; private TdsParserStateObject _stateObj; private List<_ColumnMapping> _sortedColumnMappings; - private HashSet _destColumnNames; private SqlRowsCopiedEventHandler _rowsCopiedEventHandler; @@ -613,7 +612,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i throw SQL.BulkLoadExistingTransaction(); } - _destColumnNames = new HashSet(); + HashSet destColumnNames = new HashSet(); // Loop over the metadata for each column _SqlMetaDataSet metaDataSet = internalResults[MetaDataResultId].MetaData; @@ -647,7 +646,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i } _sortedColumnMappings.Add(new _ColumnMapping(_localColumnMappings[assocId]._internalSourceColumnOrdinal, metadata)); - _destColumnNames.Add(metadata.column); + destColumnNames.Add(metadata.column); nmatched++; if (nmatched > 1) @@ -819,21 +818,21 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i } if (ColumnOrderHints.Count > 0) { - updateBulkCommandText.Append((addSeparator ? ", " : "") + TryGetOrderHintText()); + updateBulkCommandText.Append((addSeparator ? ", " : "") + TryGetOrderHintText(destColumnNames)); } updateBulkCommandText.Append(")"); } return (updateBulkCommandText.ToString()); } - private string TryGetOrderHintText() + private string TryGetOrderHintText(HashSet destColumnNames) { StringBuilder orderHintText = new StringBuilder("ORDER("); foreach (SqlBulkCopyColumnOrderHint orderHint in ColumnOrderHints) { string columnNameArg = orderHint.Column; - if (!_destColumnNames.Contains(columnNameArg)) + if (!destColumnNames.Contains(columnNameArg)) { // column is not valid in the destination table throw SQL.BulkLoadOrderHintInvalidColumn(columnNameArg); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index b22727b780..37a4ff7c30 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -291,7 +291,6 @@ private int RowNumber private TdsParser _parser; private TdsParserStateObject _stateObj; private List<_ColumnMapping> _sortedColumnMappings; - private HashSet _destColumnNames; private SqlRowsCopiedEventHandler _rowsCopiedEventHandler; @@ -701,7 +700,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i throw SQL.BulkLoadExistingTransaction(); } - _destColumnNames = new HashSet(); + HashSet destColumnNames = new HashSet(); // loop over the metadata for each column // @@ -737,7 +736,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i } _sortedColumnMappings.Add(new _ColumnMapping(_localColumnMappings[assocId]._internalSourceColumnOrdinal, metadata)); - _destColumnNames.Add(metadata.column); + destColumnNames.Add(metadata.column); nmatched++; if (nmatched > 1) @@ -921,21 +920,21 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i } if (ColumnOrderHints.Count > 0) { - updateBulkCommandText.Append((addSeparator ? ", " : "") + TryGetOrderHintText()); + updateBulkCommandText.Append((addSeparator ? ", " : "") + TryGetOrderHintText(destColumnNames)); } updateBulkCommandText.Append(")"); } return (updateBulkCommandText.ToString()); } - private string TryGetOrderHintText() + private string TryGetOrderHintText(HashSet destColumnNames) { StringBuilder orderHintText = new StringBuilder("ORDER("); foreach (SqlBulkCopyColumnOrderHint orderHint in ColumnOrderHints) { string columnNameArg = orderHint.Column; - if (!_destColumnNames.Contains(columnNameArg)) + if (!destColumnNames.Contains(columnNameArg)) { // column is not valid in the destination table throw SQL.BulkLoadOrderHintInvalidColumn(columnNameArg); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs index 9fbb131baf..235fc1706c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs @@ -36,14 +36,14 @@ public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint /// public SqlBulkCopyColumnOrderHint Add(string column, SortOrder sortOrder) => Add(new SqlBulkCopyColumnOrderHint(column, sortOrder)); - /// - public new void Clear() +#pragma warning disable CS1591 // XML comment not needed. Method is not part of public API and class is sealed + protected override void OnClear() +#pragma warning restore CS1591 { foreach (SqlBulkCopyColumnOrderHint orderHint in InnerList) { UnregisterColumnName(orderHint, orderHint.Column); } - base.Clear(); } /// @@ -78,16 +78,22 @@ public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) { throw new ArgumentNullException(nameof(columnOrderHint)); } - UnregisterColumnName(columnOrderHint, columnOrderHint.Column); - InnerList.Remove(columnOrderHint); + // OnRemove only works with the List instance and not the InnerList instance + List.Remove(columnOrderHint); } - /// - public new void RemoveAt(int index) +#pragma warning disable CS1591 // XML comment not needed. Method is not part of public API and class is sealed + protected override void OnRemove(int index, object value) +#pragma warning restore CS1591 { - var orderHint = (SqlBulkCopyColumnOrderHint)InnerList[index]; - UnregisterColumnName(orderHint, orderHint.Column); - base.RemoveAt(index); + if (value is SqlBulkCopyColumnOrderHint orderHint) + { + UnregisterColumnName(orderHint, orderHint.Column); + } + else + { + throw new ArgumentNullException(nameof(orderHint)); + } } private void ColumnNameChanging(object sender, string newName) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs index 57bb81ba0c..b6104335dd 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlBulkCopyColumnOrderHintCollectionTest.cs @@ -428,11 +428,8 @@ public void Remove_BehavesAsExpected() collection.Remove(item2); Assert.Empty(collection); - // The explicit implementation of IList.Remove throws ArgumentException if - // the item isn't in the collection, but the public Remove method does not - // throw in the full framework. - collection.Remove(item2); - collection.Remove(new SqlBulkCopyColumnOrderHint("column3", SortOrder.Descending)); + AssertExtensions.Throws(() => collection.Remove(item2)); + AssertExtensions.Throws(() => collection.Remove(new SqlBulkCopyColumnOrderHint("column3", SortOrder.Descending))); IList list = CreateCollection(item1, item2); @@ -547,10 +544,19 @@ public void Add_DuplicateColumnNames_NotAllowed() collection1.Add("column1", SortOrder.Descending); collection1.Add("column2", SortOrder.Descending); collection1.Add("column3", SortOrder.Descending); - collection1.Add("column4", SortOrder.Descending); - collection1.Add("column5", SortOrder.Descending); collection2[0].Column = collection1[0].Column; Assert.Throws(() => collection1.Add(collection2[0])); + ValidateCollection(collection1, expectedCount: 3); + collection1.RemoveAt(0); + collection1.Add(collection2[0]); + collection1.Remove(collection1[collection1.Count - 1]); + collection1.RemoveAt(1); + collection1.Remove(collection1[collection1.Count - 1]); + collection1.Add("column1", SortOrder.Descending); + collection1.Add("column2", SortOrder.Descending); + collection1.Add("column3", SortOrder.Descending); + collection1.Add("column4", SortOrder.Descending); + collection1.Add("column5", SortOrder.Descending); ValidateCollection(collection1, expectedCount: 5); } From 8ceb9fb1bc319702b5090627a558ef9c56452092 Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Tue, 9 Jun 2020 17:07:30 -0700 Subject: [PATCH 33/34] XML comments --- .../SqlClient/SqlBulkCopyColumnOrderHintCollection.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs index 235fc1706c..b49b6666d9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs @@ -36,9 +36,10 @@ public SqlBulkCopyColumnOrderHint Add(SqlBulkCopyColumnOrderHint columnOrderHint /// public SqlBulkCopyColumnOrderHint Add(string column, SortOrder sortOrder) => Add(new SqlBulkCopyColumnOrderHint(column, sortOrder)); -#pragma warning disable CS1591 // XML comment not needed. Method is not part of public API and class is sealed + /// + /// Invoked before the collection is cleared using Clear(). Unregisters each order hint. + /// protected override void OnClear() -#pragma warning restore CS1591 { foreach (SqlBulkCopyColumnOrderHint orderHint in InnerList) { @@ -82,9 +83,10 @@ public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) List.Remove(columnOrderHint); } -#pragma warning disable CS1591 // XML comment not needed. Method is not part of public API and class is sealed + /// + /// Invoked before the order hint is removed using Remove() or RemoveAt(). Unregisters the order hint, + /// protected override void OnRemove(int index, object value) -#pragma warning restore CS1591 { if (value is SqlBulkCopyColumnOrderHint orderHint) { From 9c71b2f24e5b08922214fecad563f4fd2fa8e83f Mon Sep 17 00:00:00 2001 From: Johnny Pham <23270162+johnnypham@users.noreply.github.com> Date: Tue, 9 Jun 2020 17:11:09 -0700 Subject: [PATCH 34/34] Update SqlBulkCopyColumnOrderHintCollection.cs --- .../Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs index b49b6666d9..82fda1ad7d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopyColumnOrderHintCollection.cs @@ -84,7 +84,7 @@ public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) } /// - /// Invoked before the order hint is removed using Remove() or RemoveAt(). Unregisters the order hint, + /// Invoked before the order hint is removed using Remove() or RemoveAt(). Unregisters the order hint. /// protected override void OnRemove(int index, object value) {