diff --git a/src/Dapper.Bulk/DapperBulk.cs b/src/Dapper.Bulk/DapperBulk.cs index 956060c..3c366dc 100644 --- a/src/Dapper.Bulk/DapperBulk.cs +++ b/src/Dapper.Bulk/DapperBulk.cs @@ -22,14 +22,15 @@ public static class DapperBulk /// The type being inserted. /// Open SqlConnection /// Entities to insert + /// TableName /// The transaction to run under, null (the default) if none /// Number of bulk items inserted together, 0 (the default) if all /// Number of seconds before bulk command execution timeout, 30 (the default) /// Usage of db generated ids. By default DB generated IDs are used (identityInsert=false) - public static void BulkInsert(this SqlConnection connection, IEnumerable data, SqlTransaction transaction = null, int batchSize = 0, int bulkCopyTimeout = 30, bool identityInsert = false) + public static void BulkInsert(this SqlConnection connection, IEnumerable data, string tableName, SqlTransaction transaction = null, int batchSize = 0, int bulkCopyTimeout = 30, bool identityInsert = false) { var type = typeof(T); - BulkInsert(connection,type,data.Cast(),transaction,batchSize,bulkCopyTimeout,identityInsert); + BulkInsert(connection, type, data.Cast(), tableName, transaction, batchSize, bulkCopyTimeout, identityInsert); } /// @@ -39,28 +40,34 @@ public static void BulkInsert(this SqlConnection connection, IEnumerable d /// Open SqlConnection /// The type being inserted. /// Entities to insert + /// TableName /// The transaction to run under, null (the default) if none /// Number of bulk items inserted together, 0 (the default) if all /// Number of seconds before bulk command execution timeout, 30 (the default) /// Usage of db generated ids. By default DB generated IDs are used (identityInsert=false) - public static void BulkInsert(this SqlConnection connection, Type type, IEnumerable data, SqlTransaction transaction = null, int batchSize = 0, int bulkCopyTimeout = 30, bool identityInsert = false) - { - var tableName = TableMapper.GetTableName(type); + public static void BulkInsert(this SqlConnection connection, Type type, IEnumerable data, string tableName, SqlTransaction transaction = null, int batchSize = 0, int bulkCopyTimeout = 30, bool identityInsert = false) + { + tableName = string.IsNullOrWhiteSpace(tableName) ? TableMapper.GetTableName(type) : tableName; var allProperties = PropertiesCache.TypePropertiesCache(type); var keyProperties = PropertiesCache.KeyPropertiesCache(type); var computedProperties = PropertiesCache.ComputedPropertiesCache(type); var columns = PropertiesCache.GetColumnNamesCache(type); var insertProperties = allProperties.Except(computedProperties).ToList(); - + if (!identityInsert) insertProperties = insertProperties.Except(keyProperties).ToList(); var (identityInsertOn, identityInsertOff, sqlBulkCopyOptions) = GetIdentityInsertOptions(identityInsert, tableName); - + var insertPropertiesString = GetColumnsStringSqlServer(insertProperties, columns); var tempToBeInserted = $"#TempInsert_{tableName}".Replace(".", string.Empty); + if (connection.State != ConnectionState.Open) + { + connection.Open(); + } + connection.Execute($@"SELECT TOP 0 {insertPropertiesString} INTO {tempToBeInserted} FROM {FormatTableName(tableName)} target WITH(NOLOCK);", null, transaction); using (var bulkCopy = new SqlBulkCopy(connection, sqlBulkCopyOptions, transaction)) @@ -85,15 +92,16 @@ public static void BulkInsert(this SqlConnection connection, Type type, IEnumera /// The element type of the array /// Open SqlConnection /// Entities to insert + /// TableName /// The transaction to run under, null (the default) if none /// Number of bulk items inserted together, 0 (the default) if all /// Number of seconds before bulk command execution timeout, 30 (the default) /// Usage of db generated ids. By default DB generated IDs are used (identityInsert=false) /// Inserted entities - public static IEnumerable BulkInsertAndSelect(this SqlConnection connection, IEnumerable data, SqlTransaction transaction = null, int batchSize = 0, int bulkCopyTimeout = 30, bool identityInsert = false) + public static IEnumerable BulkInsertAndSelect(this SqlConnection connection, IEnumerable data, string tableName, SqlTransaction transaction = null, int batchSize = 0, int bulkCopyTimeout = 30, bool identityInsert = false) { var type = typeof(T); - var tableName = TableMapper.GetTableName(type); + tableName = string.IsNullOrWhiteSpace(tableName) ? TableMapper.GetTableName(type) : tableName; var allProperties = PropertiesCache.TypePropertiesCache(type); var keyProperties = PropertiesCache.KeyPropertiesCache(type); var computedProperties = PropertiesCache.ComputedPropertiesCache(type); @@ -102,18 +110,18 @@ public static IEnumerable BulkInsertAndSelect(this SqlConnection connectio if (keyProperties.Count == 0) { var dataList = data.ToList(); - connection.BulkInsert(dataList, transaction, batchSize, bulkCopyTimeout); + connection.BulkInsert(dataList, tableName, transaction, batchSize, bulkCopyTimeout); return dataList; } var insertProperties = allProperties.Except(computedProperties).ToList(); - + if (!identityInsert) insertProperties = insertProperties.Except(keyProperties).ToList(); var (identityInsertOn, identityInsertOff, sqlBulkCopyOptions) = GetIdentityInsertOptions(identityInsert, tableName); - - var keyPropertiesString = GetColumnsStringSqlServer(keyProperties,columns); + + var keyPropertiesString = GetColumnsStringSqlServer(keyProperties, columns); var keyPropertiesInsertedString = GetColumnsStringSqlServer(keyProperties, columns, "inserted."); var insertPropertiesString = GetColumnsStringSqlServer(insertProperties, columns); var allPropertiesString = GetColumnsStringSqlServer(allProperties, columns, "target."); @@ -121,6 +129,11 @@ public static IEnumerable BulkInsertAndSelect(this SqlConnection connectio var tempToBeInserted = $"#TempInsert_{tableName}".Replace(".", string.Empty); var tempInsertedWithIdentity = $"@TempInserted_{tableName}".Replace(".", string.Empty); + if (connection.State != ConnectionState.Open) + { + connection.Open(); + } + connection.Execute($"SELECT TOP 0 {insertPropertiesString} INTO {tempToBeInserted} FROM {FormatTableName(tableName)} target WITH(NOLOCK);", null, transaction); using (var bulkCopy = new SqlBulkCopy(connection, sqlBulkCopyOptions, transaction)) @@ -133,7 +146,7 @@ public static IEnumerable BulkInsertAndSelect(this SqlConnection connectio var table = string.Join(", ", keyProperties.Select(k => $"[{(columns.ContainsKey(k.Name) ? columns[k.Name] : k.Name)}] bigint")); var joinOn = string.Join(" AND ", keyProperties.Select(k => $"target.[{(columns.ContainsKey(k.Name) ? columns[k.Name] : k.Name)}] = ins.[{(columns.ContainsKey(k.Name) ? columns[k.Name] : k.Name)}]")); - + return connection.Query($@" {identityInsertOn} DECLARE {tempInsertedWithIdentity} TABLE ({table}) @@ -154,29 +167,35 @@ public static IEnumerable BulkInsertAndSelect(this SqlConnection connectio /// The type being inserted. /// Open SqlConnection /// Entities to insert + /// TableName /// The transaction to run under, null (the default) if none /// Number of bulk items inserted together, 0 (the default) if all /// Number of seconds before bulk command execution timeout, 30 (the default) /// Usage of db generated ids. By default DB generated IDs are used (identityInsert=false) - public static async Task BulkInsertAsync(this SqlConnection connection, IEnumerable data, SqlTransaction transaction = null, int batchSize = 0, int bulkCopyTimeout = 30, bool identityInsert = false) + public static async Task BulkInsertAsync(this SqlConnection connection, IEnumerable data, string tableName, SqlTransaction transaction = null, int batchSize = 0, int bulkCopyTimeout = 30, bool identityInsert = false) { var type = typeof(T); - var tableName = TableMapper.GetTableName(type); + tableName = string.IsNullOrWhiteSpace(tableName) ? TableMapper.GetTableName(type) : tableName; var allProperties = PropertiesCache.TypePropertiesCache(type); var keyProperties = PropertiesCache.KeyPropertiesCache(type); var computedProperties = PropertiesCache.ComputedPropertiesCache(type); var columns = PropertiesCache.GetColumnNamesCache(type); var insertProperties = allProperties.Except(computedProperties).ToList(); - + if (!identityInsert) insertProperties = insertProperties.Except(keyProperties).ToList(); var (identityInsertOn, identityInsertOff, sqlBulkCopyOptions) = GetIdentityInsertOptions(identityInsert, tableName); - - var insertPropertiesString = GetColumnsStringSqlServer(insertProperties,columns); + + var insertPropertiesString = GetColumnsStringSqlServer(insertProperties, columns); var tempToBeInserted = $"#TempInsert_{tableName}".Replace(".", string.Empty); + if (connection.State != ConnectionState.Open) + { + connection.Open(); + } + await connection.ExecuteAsync($@"SELECT TOP 0 {insertPropertiesString} INTO {tempToBeInserted} FROM {FormatTableName(tableName)} target WITH(NOLOCK);", null, transaction); using (var bulkCopy = new SqlBulkCopy(connection, sqlBulkCopyOptions, transaction)) @@ -202,15 +221,16 @@ await connection.ExecuteAsync($@" /// The type being inserted. /// Open SqlConnection /// Entities to insert + /// TableName /// The transaction to run under, null (the default) if none /// Number of bulk items inserted together, 0 (the default) if all /// Number of seconds before bulk command execution timeout, 30 (the default) /// Usage of db generated ids. By default DB generated IDs are used (identityInsert=false) /// Inserted entities - public static async Task> BulkInsertAndSelectAsync(this SqlConnection connection, IEnumerable data, SqlTransaction transaction = null, int batchSize = 0, int bulkCopyTimeout = 30, bool identityInsert = false) + public static async Task> BulkInsertAndSelectAsync(this SqlConnection connection, IEnumerable data, string tableName, SqlTransaction transaction = null, int batchSize = 0, int bulkCopyTimeout = 30, bool identityInsert = false) { var type = typeof(T); - var tableName = TableMapper.GetTableName(type); + tableName = string.IsNullOrWhiteSpace(tableName) ? TableMapper.GetTableName(type) : tableName; var allProperties = PropertiesCache.TypePropertiesCache(type); var keyProperties = PropertiesCache.KeyPropertiesCache(type); var computedProperties = PropertiesCache.ComputedPropertiesCache(type); @@ -219,28 +239,33 @@ public static async Task> BulkInsertAndSelectAsync(this SqlCon if (keyProperties.Count == 0) { var dataList = data.ToList(); - await connection.BulkInsertAsync(dataList, transaction, batchSize, bulkCopyTimeout); + await connection.BulkInsertAsync(dataList, tableName, transaction, batchSize, bulkCopyTimeout); return dataList; } var insertProperties = allProperties.Except(computedProperties).ToList(); - + if (!identityInsert) insertProperties = insertProperties.Except(keyProperties).ToList(); var (identityInsertOn, identityInsertOff, sqlBulkCopyOptions) = GetIdentityInsertOptions(identityInsert, tableName); - - var keyPropertiesString = GetColumnsStringSqlServer(keyProperties,columns); - var keyPropertiesInsertedString = GetColumnsStringSqlServer(keyProperties,columns, "inserted."); - var insertPropertiesString = GetColumnsStringSqlServer(insertProperties,columns); + + var keyPropertiesString = GetColumnsStringSqlServer(keyProperties, columns); + var keyPropertiesInsertedString = GetColumnsStringSqlServer(keyProperties, columns, "inserted."); + var insertPropertiesString = GetColumnsStringSqlServer(insertProperties, columns); var allPropertiesString = GetColumnsStringSqlServer(allProperties, columns, "target."); var tempToBeInserted = $"#TempInsert_{tableName}".Replace(".", string.Empty); var tempInsertedWithIdentity = $"@TempInserted_{tableName}".Replace(".", string.Empty); + if (connection.State != ConnectionState.Open) + { + connection.Open(); + } + await connection.ExecuteAsync($@"SELECT TOP 0 {insertPropertiesString} INTO {tempToBeInserted} FROM {FormatTableName(tableName)} target WITH(NOLOCK);", null, transaction); - using (var bulkCopy = new SqlBulkCopy(connection,sqlBulkCopyOptions, transaction)) + using (var bulkCopy = new SqlBulkCopy(connection, sqlBulkCopyOptions, transaction)) { bulkCopy.BulkCopyTimeout = bulkCopyTimeout; bulkCopy.BatchSize = batchSize; @@ -272,7 +297,7 @@ private static string GetColumnsStringSqlServer(IEnumerable proper return string.Join(", ", properties.Select(property => $"{tablePrefix}[{columnNames[property.Name]}] ")); } - + private static DataTable ToDataTable(IEnumerable data, IList properties) { var typeCasts = new Type[properties.Count]; @@ -339,9 +364,9 @@ internal static string FormatTableName(string table) } private static (string identityInsertOn, string identityInsertOff, SqlBulkCopyOptions bulkCopyOptions) - GetIdentityInsertOptions(bool identityInsert, string tableName) - => identityInsert - ? ($"SET IDENTITY_INSERT {FormatTableName(tableName)} ON", - $"SET IDENTITY_INSERT {FormatTableName(tableName)} OFF", SqlBulkCopyOptions.KeepIdentity) - : (string.Empty, string.Empty, SqlBulkCopyOptions.Default); + GetIdentityInsertOptions(bool identityInsert, string tableName)=> (string.Empty, string.Empty, SqlBulkCopyOptions.Default); + //=> identityInsert + // ? ($"SET IDENTITY_INSERT {FormatTableName(tableName)} ON", + // $"SET IDENTITY_INSERT {FormatTableName(tableName)} OFF", SqlBulkCopyOptions.KeepIdentity) + // : (string.Empty, string.Empty, SqlBulkCopyOptions.Default); }