public void TestColumnMappings() { var reader = new BulkCopyDataReader <Student>(new List <Student>()); Assert.Equal(2, reader.ColumnMappings.Count); var ps = TypeExtensions.GetProperties(typeof(Student), BindingFlags.Instance | BindingFlags.Public).ToList(); for (int i = 0; i < ps.Count; i++) { Assert.Equal(ps[i].Name, reader.ColumnMappings[i].SourceColumn); Assert.Equal(ps[i].Name, reader.ColumnMappings[i].DestinationColumn); } }
/// <summary>Gets the <see cref="SqlBulkCopyInvalidConversionException"/> /// that is thrown when an invalid conversion occurs.</summary> /// <param name="e">The <see cref="Exception"/> /// that was thrown.</param> /// <param name="reader">The <see cref="BulkCopyDataReader{T}"/> /// instance that contains extra information about the /// items being yielded for bulk copy.</param> /// <returns>The <see cref="SqlBulkCopyInvalidConversionException"/> /// that should be thrown, or null if not applicable.</returns> internal static SqlBulkCopyInvalidConversionException GetBulkCopyInvalidConversionException(Exception e, BulkCopyDataReader <T> reader) { // Validate parameters. Debug.Assert(e != null); Debug.Assert(reader != null); // Get the invalid operation exception. var ioe = e as InvalidOperationException; // If null, get out. if (ioe == null) { return(null); } // The regular expression match. Match match = InvalidConversionRegex.Match(ioe.Message); // Match exception message. if (!match.Success) { return(null); } // Get the row and the item from the reader, and feed into the exception. return(new SqlBulkCopyInvalidConversionException(ioe.Message, ioe, reader._enumerator.Current, reader._itemsRead, match.Groups["sourceType"].Value, match.Groups["destinationType"].Value)); }
/// <summary>Bulk copies a sequence of instances of <typeparamref name="T"/> to the database.</summary> /// <typeparam name="T">The type that is bulk copied to the database.</typeparam> /// <remarks> /// <para>Reflection is not used for getting the values from the <paramref name="items"/> /// sequence; reflection is used to perform the mapping, but the mapping itself is compiled /// code generated on-the-fly for performance.</para> /// <para>The name of the type is used as the table name (with "dbo" as the default schema), or /// if the <see cref="TableAttribute"/> is applied, then the <see cref="TableAttribute.Name"/> /// property is used.</para> /// <para>The names of the public properties are mapped to the names of the columns, or, if the /// property has the <see cref="ColumnAttribute"/> applied to it, then /// <see cref="ColumnAttribute.Name"/> is used.</para> /// </remarks> /// <param name="connection">The <see cref="SqlConnection"/> that is used to perform the bulk copy.</param> /// <param name="items">The sequence of instances of <typeparamref name="T"/> /// to bulk copy to the database.</param> /// <param name="commandTimeout">The timeout to assign to the operation.</param> /// <param name="sqlBulkCopyOptions">Values from the <see cref="SqlBulkCopyOptions"/> /// that set the options when bulk copying to the database.</param> /// <param name="table">The name of the table that the <paramref name="items"/> /// should be copied to.</param> /// <param name="batchSize">The size of the batch to send to SQL server.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to /// cancel the operation, if necessary.</param> public static async Task SqlBulkCopyAsync <T>(this SqlConnection connection, IEnumerable <T> items, string table, TimeSpan commandTimeout, int batchSize, SqlBulkCopyOptions sqlBulkCopyOptions, CancellationToken cancellationToken) { // Validate the parameters. if (connection == null) { throw new ArgumentNullException(nameof(connection)); } if (items == null) { throw new ArgumentNullException(nameof(items)); } if (string.IsNullOrWhiteSpace(table)) { throw new ArgumentNullException(nameof(table)); } // Validate the batch size. if (batchSize < 0) { throw new ArgumentOutOfRangeException(nameof(batchSize), batchSize, "The batchSize parameter must be a non-negative value."); } // Get the command timeout in seconds. var commandTimeoutSeconds = (int)commandTimeout.TotalSeconds; // If 0 or less, throw an exception. if (commandTimeoutSeconds <= 0) { throw new ArgumentOutOfRangeException(nameof(commandTimeout), commandTimeout, "The commandTimeout parameter must be a positive value."); } // Create the sql bulk copy. using (var bc = new SqlBulkCopy(connection, sqlBulkCopyOptions, null)) { // Create the reader. var reader = new BulkCopyDataReader <T>(items); // Initialize bulk copier. bc.BatchSize = batchSize; bc.BulkCopyTimeout = commandTimeoutSeconds; // Set properties from the reader. Destination table name. bc.DestinationTableName = table; // Configure mappings, this is from the field name in the // destination to an index-based lookup in the source (the // enumerables). bc.ColumnMappings.Clear(); foreach (KeyValuePair <int, BulkCopyDataReader <T> .Mapping> mapping in BulkCopyDataReader <T> .OrdinalToMappingMap) { bc.ColumnMappings.Add(mapping.Key, mapping.Value.Column); } // Wrap in a try/catch. try { // Write the values. await bc.WriteToServerAsync(reader, cancellationToken).ConfigureAwait(false); } catch (Exception e) { // The exception to throw. Exception exceptionToThrow = null; // Try to parse, if there's an exception, throw the original. try { // Try and get the BulkException. exceptionToThrow = BulkCopyDataReader <T> .GetBulkCopyColumnIdException(e, connection, table); // Try and get the column mappings exception. exceptionToThrow = exceptionToThrow ?? BulkCopyDataReader <T> .GetBulkCopyInvalidColumnMappingsException(e, bc); // Try and get the invalid conversion exception. exceptionToThrow = exceptionToThrow ?? BulkCopyDataReader <T> .GetBulkCopyInvalidConversionException(e, reader); } catch { // Intentionally do nothing. } // Throw the exception if not null. if (exceptionToThrow != null) { throw exceptionToThrow; } // Throw the original exception. throw; } } }