From ec5fb17ad9780e89a1108fb18f09b75f96d3ec5c Mon Sep 17 00:00:00 2001 From: ErikEJ Date: Sat, 13 May 2017 19:11:07 +0200 Subject: [PATCH 1/2] Ignore duplicate key errors DRY fixes #7 --- src/Properties/AssemblyInfo.cs | 4 +- src/SqlCeBulkCopy.cs | 71 +++++++++++++++------------ src/SqlCeBulkCopyMappingCollection.cs | 11 ++--- src/SqlCeBulkCopyOptions.cs | 4 ++ src/SqlCeBulkCopyTableHelpers.cs | 5 -- src/SqlCeRowsCopiedEventArgs.cs | 15 +----- src/Test/SqlCeBulkCopyTest.cs | 22 +++++++-- 7 files changed, 71 insertions(+), 61 deletions(-) diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs index 836874c..825f358 100644 --- a/src/Properties/AssemblyInfo.cs +++ b/src/Properties/AssemblyInfo.cs @@ -18,8 +18,8 @@ // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("1c320ebd-6449-44eb-b727-fae56de65eaa")] -[assembly: AssemblyVersion("2.1.6.14")] +[assembly: AssemblyVersion("2.1.6.15")] #if PocketPC #else -[assembly: AssemblyFileVersion("2.1.6.14")] +[assembly: AssemblyFileVersion("2.1.6.15")] #endif \ No newline at end of file diff --git a/src/SqlCeBulkCopy.cs b/src/SqlCeBulkCopy.cs index d87528d..573f637 100644 --- a/src/SqlCeBulkCopy.cs +++ b/src/SqlCeBulkCopy.cs @@ -26,14 +26,14 @@ public class SqlCeBulkCopy : IDisposable private readonly SqlCeTransaction _trans; private readonly bool _ownsConnection; private bool _ownsTransaction; - private readonly bool _keepNulls; - private readonly bool _keepIdentity; + private bool _keepNulls; + private bool _keepIdentity; + private bool _ignoreDuplicateErrors; private string _destination; - private readonly SqlCeBulkCopyOptions _sqlCeBulkCopyOptions; #if PocketPC #else private List _savedConstraints; - private readonly bool _disableConstraints; + private bool _disableConstraints; #endif /// /// Initializes a new instance of the SqlCeBulkCopy class using the specified open instance of SqlCeConnection. @@ -62,13 +62,7 @@ public SqlCeBulkCopy(SqlCeConnection connection, SqlCeTransaction transaction) public SqlCeBulkCopy(SqlCeConnection connection, SqlCeBulkCopyOptions copyOptions) { _conn = connection; - _sqlCeBulkCopyOptions = copyOptions; - _keepNulls = IsCopyOption(SqlCeBulkCopyOptions.KeepNulls); - _keepIdentity = IsCopyOption(SqlCeBulkCopyOptions.KeepIdentity); - #if PocketPC -#else - _disableConstraints = IsCopyOption(SqlCeBulkCopyOptions.DisableConstraints); -#endif + SetOptions(copyOptions); } /// @@ -81,13 +75,7 @@ public SqlCeBulkCopy(SqlCeConnection connection, SqlCeBulkCopyOptions copyOption { _conn = connection; _trans = transaction; - _sqlCeBulkCopyOptions = copyOptions; - _keepNulls = IsCopyOption(SqlCeBulkCopyOptions.KeepNulls); - _keepIdentity = IsCopyOption(SqlCeBulkCopyOptions.KeepIdentity); -#if PocketPC -#else - _disableConstraints = IsCopyOption(SqlCeBulkCopyOptions.DisableConstraints); -#endif + SetOptions(copyOptions); } /// @@ -108,13 +96,7 @@ public SqlCeBulkCopy(string connectionString, SqlCeBulkCopyOptions copyOptions) { _conn = new SqlCeConnection(connectionString); _ownsConnection = true; - _sqlCeBulkCopyOptions = copyOptions; - _keepNulls = IsCopyOption(SqlCeBulkCopyOptions.KeepNulls); - _keepIdentity = IsCopyOption(SqlCeBulkCopyOptions.KeepIdentity); - #if PocketPC -#else - _disableConstraints = IsCopyOption(SqlCeBulkCopyOptions.DisableConstraints); -#endif + SetOptions(copyOptions); } /// @@ -260,6 +242,18 @@ public void WriteToServer(IEnumerable collection, Type elementType) } } #endif + + private void SetOptions(SqlCeBulkCopyOptions options) + { + _keepNulls = IsCopyOption(SqlCeBulkCopyOptions.KeepNulls, options); + _keepIdentity = IsCopyOption(SqlCeBulkCopyOptions.KeepIdentity, options); + _ignoreDuplicateErrors = IsCopyOption(SqlCeBulkCopyOptions.IgnoreDuplicateErrors, options); +#if PocketPC +#else + _disableConstraints = IsCopyOption(SqlCeBulkCopyOptions.DisableConstraints, options); +#endif + } + private void WriteToServer(ISqlCeBulkCopyInsertAdapter adapter) { CheckDestination(); @@ -278,12 +272,12 @@ private void WriteToServer(ISqlCeBulkCopyInsertAdapter adapter) if (ColumnMappings.Count > 0) { //mapping are set, and should be validated - map = ColumnMappings.ValidateCollection(_conn, localTrans, adapter, _sqlCeBulkCopyOptions, _destination); + map = ColumnMappings.ValidateCollection(_conn, localTrans, adapter, _keepNulls, _destination); } else { //create default column mappings - map = SqlCeBulkCopyColumnMappingCollection.Create(_conn, localTrans, adapter, _sqlCeBulkCopyOptions, _destination); + map = SqlCeBulkCopyColumnMappingCollection.Create(_conn, localTrans, adapter, _keepNulls, _destination); } using (var cmd = new SqlCeCommand(_destination, _conn, localTrans)) @@ -340,12 +334,27 @@ private void WriteToServer(ISqlCeBulkCopyInsertAdapter adapter) rec.SetDefault(destIndex); } } - // Fire event if needed } rowCounter++; totalRows++; - rs.Insert(rec); + try + { + rs.Insert(rec); + } + catch (SqlCeException ex) + { + if (ex.NativeError == 25016 && _ignoreDuplicateErrors) //A duplicate value cannot be inserted into a unique index. + { + System.Diagnostics.Trace.Write("Duplicate value error ignored"); + continue; + } + else + { + throw; + } + } + // Fire event if needed if (RowsCopied != null && _notifyAfter > 0 && rowCounter == _notifyAfter) { FireRowsCopiedEvent(totalRows); @@ -511,9 +520,9 @@ private void FireRowsCopiedEvent(long rowsCopied) OnRowsCopied(args); } - private bool IsCopyOption(SqlCeBulkCopyOptions copyOption) + private bool IsCopyOption(SqlCeBulkCopyOptions copyOption, SqlCeBulkCopyOptions options) { - return ((_sqlCeBulkCopyOptions & copyOption) == copyOption); + return ((options & copyOption) == copyOption); } #region IDisposable Members diff --git a/src/SqlCeBulkCopyMappingCollection.cs b/src/SqlCeBulkCopyMappingCollection.cs index 4e0856c..acd2080 100644 --- a/src/SqlCeBulkCopyMappingCollection.cs +++ b/src/SqlCeBulkCopyMappingCollection.cs @@ -77,9 +77,8 @@ internal SqlCeBulkCopyColumnMappingCollection() #region other methods - internal static List> Create(SqlCeConnection conn, SqlCeTransaction transaction, ISqlCeBulkCopyInsertAdapter adapter, SqlCeBulkCopyOptions copyOptions, string tableName) + internal static List> Create(SqlCeConnection conn, SqlCeTransaction transaction, ISqlCeBulkCopyInsertAdapter adapter, bool keepNulls, string tableName) { - var keepNulls = SqlCeBulkCopyTableHelpers.IsCopyOption(SqlCeBulkCopyOptions.KeepNulls, copyOptions); var retVal = new List>(); //we use this to determine if we throw an error while building maps. int idOrdinal = SqlCeBulkCopyTableHelpers.IdentityOrdinal(conn, transaction, tableName); @@ -121,7 +120,7 @@ internal SqlCeBulkCopyColumnMappingCollection() return retVal; } - internal List> ValidateCollection(SqlCeConnection conn, SqlCeTransaction transaction, ISqlCeBulkCopyInsertAdapter adapter, SqlCeBulkCopyOptions copyOptions, string tableName) + internal List> ValidateCollection(SqlCeConnection conn, SqlCeTransaction transaction, ISqlCeBulkCopyInsertAdapter adapter, bool keepNulls, string tableName) { if (Count > 0) { @@ -133,8 +132,8 @@ internal SqlCeBulkCopyColumnMappingCollection() { var sourceColumnName = (mapping.SourceColumn ?? string.Empty).ToUpper(CultureInfo.InvariantCulture); var destColumnName = (mapping.DestinationColumn ?? string.Empty).ToUpper(CultureInfo.InvariantCulture); - int sourceIndex = -1; - int destIndex = -1; + int sourceIndex; + int destIndex; //verify if we have a source column name that it exists if (!string.IsNullOrEmpty(sourceColumnName)) @@ -204,7 +203,7 @@ internal SqlCeBulkCopyColumnMappingCollection() } else { - return Create(conn, transaction, adapter, copyOptions, tableName); + return Create(conn, transaction, adapter, keepNulls, tableName); } } diff --git a/src/SqlCeBulkCopyOptions.cs b/src/SqlCeBulkCopyOptions.cs index c9abd59..b4a87bd 100644 --- a/src/SqlCeBulkCopyOptions.cs +++ b/src/SqlCeBulkCopyOptions.cs @@ -30,5 +30,9 @@ public enum SqlCeBulkCopyOptions /// Preserve null values in the destination table regardless of the settings for default values. When not specified, null values are replaced by default values where applicable. /// KeepNulls = 0x8, + /// + /// Ignore error 25016 - A duplicate value cannot be inserted into a unique index + /// + IgnoreDuplicateErrors = 0x16 } } diff --git a/src/SqlCeBulkCopyTableHelpers.cs b/src/SqlCeBulkCopyTableHelpers.cs index 30e9df8..58a8aa1 100644 --- a/src/SqlCeBulkCopyTableHelpers.cs +++ b/src/SqlCeBulkCopyTableHelpers.cs @@ -35,10 +35,5 @@ internal static long GetAutoIncNext(SqlCeConnection conn, string destinationTabl return 0; } } - - internal static bool IsCopyOption(SqlCeBulkCopyOptions options, SqlCeBulkCopyOptions copyOption) - { - return ((options & copyOption) == options); - } } } diff --git a/src/SqlCeRowsCopiedEventArgs.cs b/src/SqlCeRowsCopiedEventArgs.cs index 09d0099..6111b55 100644 --- a/src/SqlCeRowsCopiedEventArgs.cs +++ b/src/SqlCeRowsCopiedEventArgs.cs @@ -8,8 +8,7 @@ namespace ErikEJ.SqlCe [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ce")] public class SqlCeRowsCopiedEventArgs : EventArgs { - private long _rowsCopied; - private bool _abort; + private readonly long _rowsCopied; /// /// Represents the set of arguments passed to the SqlCeRowsCopiedEventHandler. @@ -33,16 +32,6 @@ public long RowsCopied /// /// Gets or sets a value that indicates whether the bulk copy operation should be aborted. /// - public bool Abort - { - get - { - return _abort; - } - set - { - _abort = value; - } - } + public bool Abort { get; set; } } } diff --git a/src/Test/SqlCeBulkCopyTest.cs b/src/Test/SqlCeBulkCopyTest.cs index 0cc70a6..6c70a1d 100644 --- a/src/Test/SqlCeBulkCopyTest.cs +++ b/src/Test/SqlCeBulkCopyTest.cs @@ -19,6 +19,7 @@ private enum SchemaType { NoConstraints, FullConstraints, + FullConstraintsDuplicateRows, FullNoIdentity, DataReaderTest, DataReaderTestMapped, @@ -56,6 +57,8 @@ public void ExerciseEngineWithTable() RunDataTableTest(10000, SchemaType.FullConstraints, true); + RunDataTableTest(10000, SchemaType.FullConstraintsDuplicateRows, true); + //RunDataTableTestNoKeepId(10000, SchemaType.FullNoIdentity, false); } @@ -123,6 +126,10 @@ private void RunDataTableTest(int recordCount, SchemaType schemaType, bool keepN testTable.Rows.Add(new object[] { 4 + i, Guid.NewGuid().ToString() }); } } + if (schemaType == SchemaType.FullConstraintsDuplicateRows) + { + testTable.Rows.Add(new object[] { 4, Guid.NewGuid().ToString() }); + } RunBulkCopy(schemaType, keepNulls, testTable); } } @@ -236,15 +243,21 @@ private static void RunBulkCopy(SchemaType schemaType, bool keepNulls, DataTable case SchemaType.FullConstraints: options = SqlCeBulkCopyOptions.KeepIdentity; break; - default: + case SchemaType.FullConstraintsDuplicateRows: + options = SqlCeBulkCopyOptions.KeepIdentity; break; } if (keepNulls) { - options = options |= SqlCeBulkCopyOptions.KeepNulls; + options = options |= SqlCeBulkCopyOptions.KeepNulls; } - - System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); + + if (schemaType == SchemaType.FullConstraintsDuplicateRows) + { + options = options |= SqlCeBulkCopyOptions.IgnoreDuplicateErrors; + } + + var sw = new Stopwatch(); sw.Start(); using (SqlCeBulkCopy bc = new SqlCeBulkCopy(connectionString, options)) @@ -285,6 +298,7 @@ private static void CreateDatabase(SchemaType schemaType) break; case SchemaType.FullConstraints: + case SchemaType.FullConstraintsDuplicateRows: cmd.CommandText = "CREATE TABLE [Shippers] ([ShipperID] int NOT NULL IDENTITY (1,1), [CompanyName] nvarchar(40) NULL DEFAULT N'ABC');"; cmd.ExecuteNonQuery(); From 911a15853277b3640d0f7a0ed055b60bad3307bd Mon Sep 17 00:00:00 2001 From: ErikEJ Date: Sun, 14 May 2017 18:17:38 +0200 Subject: [PATCH 2/2] Small clarifications --- src/SqlCeBulkCopy.cs | 2 +- src/SqlCeBulkCopyOptions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SqlCeBulkCopy.cs b/src/SqlCeBulkCopy.cs index 573f637..9842bbf 100644 --- a/src/SqlCeBulkCopy.cs +++ b/src/SqlCeBulkCopy.cs @@ -346,7 +346,7 @@ private void WriteToServer(ISqlCeBulkCopyInsertAdapter adapter) { if (ex.NativeError == 25016 && _ignoreDuplicateErrors) //A duplicate value cannot be inserted into a unique index. { - System.Diagnostics.Trace.Write("Duplicate value error ignored"); + System.Diagnostics.Trace.TraceWarning("SqlCeBulkCopy: Duplicate value error was ignored"); continue; } else diff --git a/src/SqlCeBulkCopyOptions.cs b/src/SqlCeBulkCopyOptions.cs index b4a87bd..0bce3a3 100644 --- a/src/SqlCeBulkCopyOptions.cs +++ b/src/SqlCeBulkCopyOptions.cs @@ -31,7 +31,7 @@ public enum SqlCeBulkCopyOptions /// KeepNulls = 0x8, /// - /// Ignore error 25016 - A duplicate value cannot be inserted into a unique index + /// Ignores error 25016 - A duplicate value cannot be inserted into a unique index /// IgnoreDuplicateErrors = 0x16 }