-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from tbudurovych/master
Add class SimpleSqlBulkUpdate to support bulk updates.
- Loading branch information
Showing
7 changed files
with
187 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
-- sample table schema | ||
CREATE TABLE [dbo].[TableName]( | ||
[Id] [int] IDENTITY(1,1) NOT NULL, | ||
[Score] [int] NULL, | ||
[Winner] [varchar](100) NULL, | ||
[CreatedOn] [datetime] NULL, | ||
[IsFinal] [bit] NOT NULL CONSTRAINT [DF_TableName_IsFinal] DEFAULT ((0)), | ||
CONSTRAINT [PK_TableName] PRIMARY KEY CLUSTERED | ||
( | ||
[Id] ASC | ||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] | ||
) ON [PRIMARY] | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using System.Collections.Generic; | ||
using System.Reflection; | ||
|
||
namespace System.Data.SqlClient | ||
{ | ||
public class Common | ||
{ | ||
public static DataTable GetDataTableFromFields<T>(IEnumerable<T> data, SqlBulkCopy sqlBulkCopy) | ||
{ | ||
var dt = new DataTable(); | ||
Type listType = typeof (T); | ||
foreach (PropertyInfo propertyInfo in listType.GetProperties()) | ||
{ | ||
dt.Columns.Add(propertyInfo.Name, propertyInfo.PropertyType); | ||
sqlBulkCopy.ColumnMappings.Add(propertyInfo.Name, propertyInfo.Name); | ||
} | ||
|
||
foreach (T value in data) | ||
{ | ||
DataRow dr = dt.NewRow(); | ||
foreach (PropertyInfo propertyInfo in listType.GetProperties()) | ||
{ | ||
dr[propertyInfo.Name] = propertyInfo.GetValue(value, null); | ||
} | ||
dt.Rows.Add(dr); | ||
} | ||
return dt; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace System.Data.SqlClient | ||
{ | ||
public class SimpleSqlBulkUpdate : IDisposable | ||
{ | ||
private readonly SqlConnection _connection; | ||
private readonly SqlTransaction _externalTransaction; | ||
private readonly ConnectionState _initialState = ConnectionState.Closed; | ||
|
||
public SimpleSqlBulkUpdate(SqlConnection connection) | ||
{ | ||
_connection = connection; | ||
_initialState = connection.State; | ||
} | ||
|
||
public SimpleSqlBulkUpdate(SqlConnection connection, | ||
SqlTransaction externalTransaction) | ||
{ | ||
_externalTransaction = externalTransaction; | ||
_connection = connection; | ||
_initialState = connection.State; | ||
} | ||
|
||
public SimpleSqlBulkUpdate(string connectionString) | ||
{ | ||
_connection = new SqlConnection(connectionString); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
// recycle connection if was not initially open | ||
if (_initialState != ConnectionState.Open | ||
&&_connection != null) | ||
_connection.Dispose(); | ||
} | ||
|
||
public void BulkUpdate<T>(string destinationTableName, IEnumerable<T> data, | ||
string columnNameToMatch, | ||
string[] columnNamesToUpdate) | ||
{ | ||
bool wasOpen = _connection.State == ConnectionState.Open; | ||
if (!wasOpen) | ||
_connection.Open(); | ||
string tempTablename = "#" + destinationTableName + "_" + Guid.NewGuid().ToString("N"); | ||
CreateTempTable(destinationTableName, tempTablename); | ||
var dataAsArray = data as T[] ?? data.ToArray(); | ||
|
||
using (SqlBulkCopy sbc = new SqlBulkCopy(_connection, SqlBulkCopyOptions.KeepIdentity, _externalTransaction)) | ||
{ | ||
sbc.DestinationTableName = tempTablename; | ||
DataTable dt = Common.GetDataTableFromFields(dataAsArray, sbc); | ||
sbc.WriteToServer(dt); | ||
} | ||
MergeTempAndDestination(destinationTableName, tempTablename, columnNameToMatch, columnNamesToUpdate); | ||
DropTempTable(tempTablename); | ||
|
||
if (!wasOpen) | ||
_connection.Close(); | ||
} | ||
|
||
private void DropTempTable(string tempTablename) | ||
{ | ||
var cmdTempTable = _connection.CreateCommand(); | ||
cmdTempTable.CommandText = "DROP TABLE " + tempTablename; | ||
cmdTempTable.ExecuteNonQuery(); | ||
} | ||
|
||
private void MergeTempAndDestination(string destinationTableName, string tempTablename, | ||
string matchingColumn, | ||
string[] columnNamesToUpdate) | ||
{ | ||
var updateSql = ""; | ||
for (var i = 0; i < columnNamesToUpdate.Length; i++) | ||
{ | ||
updateSql += String.Format("Target.[{0}]=Source.[{0}]", columnNamesToUpdate[i]); | ||
if (i < columnNamesToUpdate.Length - 1) | ||
updateSql += ","; | ||
} | ||
var mergeSql = "MERGE INTO " + destinationTableName + " AS Target\r\n" + | ||
"USING " + tempTablename + " AS Source\r\n" + | ||
"ON\r\n" + | ||
"Target." + matchingColumn + " = Source." + matchingColumn + "\r\n" + | ||
"WHEN MATCHED THEN\r\n" + | ||
"UPDATE SET " + updateSql + ";"; | ||
|
||
var cmdTempTable = _connection.CreateCommand(); | ||
cmdTempTable.CommandText = mergeSql; | ||
cmdTempTable.ExecuteNonQuery(); | ||
} | ||
|
||
private void CreateTempTable(string destinationTableName, string tempTablename) | ||
{ | ||
var cmdTempTable = _connection.CreateCommand(); | ||
cmdTempTable.CommandText = "SELECT TOP 0 * \r\n" + | ||
"INTO " + tempTablename + "\r\n" + | ||
"FROM " + destinationTableName; | ||
cmdTempTable.ExecuteNonQuery(); | ||
} | ||
} | ||
} |