Introduction
The other day, I had to make an application that makes new tables in a Microsoft SQL database and save a lot of data into these tables. I couldn't find an easy way to do this in the .NET Framework 2.0. For these reasons, I made a class that collects and saves data in a Microsoft SQL database with SqlBulkCopy
. It isn't a very complex solution, but it may save some coding time in similar situations. This project was implemented in C# with VS 2005. The database server was a Microsoft SQL Server 2000.
What Does dataCollector Do?
First, you have to define the schema of the table. You have to choose a .NET Framework type, and a SQL data type too. The dataCollector
can create the table in the DB if you want. You could declare the primary key, and auto incremental identity column if needed. The dataCollector
makes and runs the required SQL commands (create table
; alter table
). It also makes a DataTable
with the same schema. It will be the temporary storage for your data.
In the second step, you can add new DataRow
objects to the dataCollector
. If the amount of data rows in the temporary storage reaches the given value, the dataCollector
instance inserts these rows in the database table with a SqlBulkCopy
, clears the temporary storage and continues receiving data.
When you finish, you have to order the dataCollector
to save all unsaved data.
Defining the Schema of Table
Defining the schema of the table is a critical point. A small hidden mistake can cause a lot of anger.
I chose a simple but flexible way for defining the schema. An instance of dc_DataColumnDefinition
class (included in the project) represents one column of the table. See the commented code of that class for details. These objects are stored in an ArrayList
.
You have to choose the data type of column in SQL table and in DataTable
too. Watch the correct and ideal pairing of data types. For example, a string
type in C# can be char, nchar, varchar, nvarchar, text, ntext
in the database depending on the usage of that column.
The order of dc_DataColumnDefinition
instances in the ArrayList
defines the order of columns in database table, and in the DataTable
.
If you use an existing table in the DB, you have to define the same table for dataCollector
! The SqlBulkCopy
doesn't check the names, types and orders of the columns. It simply tries to insert the first value into the first field, the second value into the second field, and so on. If you try to insert a text into an integer field, the result will be an empty field, without an error message. The result is the same if you try to write an identity column, nothing will happen - you simply lose your data.
Using the Class
The following commented code shows how does the dataCollector
class works. You can find it in the example project.
private void test01()
{
string connString = "Data Source=(local);
Initial Catalog=Test;Integrated Security=SSPI;";
string tableOwner = "dbo";
string tableName = "test01";
ArrayList colList = new ArrayList();
dc.dc_DataColumnDefinition col;
col = new dc.dc_DataColumnDefinition(true, "id",
System.Type.GetType("System.Int32"), "[int]", true, 1, 1);
colList.Add(col);
col = new dc.dc_DataColumnDefinition
("str1", System.Type.GetType("System.String"), "[nvarchar](255)");
colList.Add(col);
col = new dc.dc_DataColumnDefinition
("int1", System.Type.GetType("System.Int32"), "[int]");
colList.Add(col);
dc.dataCollector dcTest = new dc.dataCollector
(connString, tableOwner, tableName, true, colList);
dcTest.insertPerRows = 3;
gl.mb(dcTest.createTableCommandText, "Create table command");
if (dcTest.createPKCommandText != "")
{
gl.mb(dcTest.createPKCommandText, "Create primary key command");
}
DataRow row;
row = dcTest.NewRow();
row[1] = "test string";
row[2] = 42;
dcTest.AddDataRow(row);
row = dcTest.NewRow();
row[1] = "*** I like Role Playing Games :) ***";
dcTest.AddDataRow(row);
row = dcTest.NewRow();
row[2] = 1;
dcTest.AddDataRow(row);
gl.mb("Before you click OK check the table
(named '" + dcTest.fullTableName + "'.)"
+ Environment.NewLine +
"The dataCollector already saved the first
three rows automatically.");
row = dcTest.NewRow();
row[1] = "'It is only with the heart that one can see rightly; ...'";
row[2] = 564;
dcTest.AddDataRow(row);
row = dcTest.NewRow();
row[1] = "'... what is essential is invisible to the eye.' -
Antoine de Saint-Exupery";
dcTest.AddDataRow(row);
dcTest.SaveUnsavedDataNow();
gl.mb("Check the table again. All five rows have been saved.");
gl.mb("End of example");
}
Code
Commented code of the dataCollector
and dc_DataColumnDefinition
classes.
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Collections;
namespace dc
{
class dataCollector
{
public int insertPerRows = 1000;
public int BatchSizeInBulkCopy = 1000;
private DataTable dt;
private SqlConnection conn;
public dataCollector(string sqlConnString, string tableOwner,
string tableName, bool createThisTable, ArrayList dataColDef)
{
connString_i = sqlConnString;
tableName_i = tableName;
tableOwner_i = tableOwner;
createPKCommandText_i = "";
dt = new DataTable(tableName);
createTableCommandText_i =
"CREATE TABLE " + fullTableName + " (" + Environment.NewLine;
foreach (dc_DataColumnDefinition colDef in dataColDef)
{
dt.Columns.Add(colDef.columnName,
colDef.columnType);
createTableCommandText_i +=
"\t[" + colDef.columnName + "] " + colDef.sqlType;
if (colDef.sqlIdentity)
{
createTableCommandText_i += " IDENTITY
(" + colDef.sqlIdentitySeed + "," +
colDef.sqlIdentityIncrement + ") NOT NULL";
}
else
{
if (colDef.sqlNullIsAllowed)
{
createTableCommandText_i +=
" NULL";
}
else
{
createTableCommandText_i +=
" NOT NULL";
}
}
createTableCommandText_i += "," +
Environment.NewLine;
if (colDef.primaryKey)
{
createPKCommandText_i = "ALTER TABLE " +
fullTableName + Environment.NewLine +
" ADD CONSTRAINT [PK_" + tableName + "]
PRIMARY KEY CLUSTERED" +
Environment.NewLine +
"([" + colDef.columnName + "])";
}
}
createTableCommandText_i = createTableCommandText_i.Remove
(createTableCommandText_i.Length -
Environment.NewLine.Length - 1);
createTableCommandText_i += ")";
conn = new SqlConnection(this.connString);
if (createThisTable)
{
SqlCommand sqlComm =
new SqlCommand(createTableCommandText_i, conn);
conn.Open();
sqlComm.ExecuteNonQuery();
if (createPKCommandText != "")
{
SqlCommand sqlCommPK =
new SqlCommand(createPKCommandText, conn);
sqlCommPK.ExecuteNonQuery();
}
conn.Close();
}
}
public DataRow NewRow()
{
return dt.NewRow();
}
public void AddDataRow(DataRow row)
{
dt.Rows.Add(row);
if (dt.Rows.Count >= insertPerRows)
{
savingDataToDB();
}
}
public void SaveUnsavedDataNow()
{
savingDataToDB();
}
private void savingDataToDB()
{
if (dt.Rows.Count > 0)
{
using (SqlBulkCopy bc = new SqlBulkCopy
(this.connString))
{
bc.BatchSize = BatchSizeInBulkCopy;
bc.DestinationTableName = fullTableName;
bc.WriteToServer(dt);
bc.Close();
}
dt.Rows.Clear();
}
}
public int ActualRowCount
{
get { return dt.Rows.Count; }
}
public void ClearRowsWithoutSaving()
{
dt.Rows.Clear();
}
private string connString_i;
public string connString
{
get { return connString_i; }
}
private string tableOwner_i;
public string tableOwner
{
get { return tableOwner_i; }
}
private string tableName_i;
public string tableName
{
get { return tableName_i; }
}
public string fullTableName
{
get { return "[" + tableOwner + "].[" + tableName + "]"; }
}
private string createTableCommandText_i;
public string createTableCommandText
{
get { return createTableCommandText_i; }
}
private string createPKCommandText_i;
public string createPKCommandText
{
get { return createPKCommandText_i; }
}
}
class dc_DataColumnDefinition
{
public bool primaryKey;
public string columnName;
public System.Type columnType;
public string sqlType;
public bool sqlNullIsAllowed;
public bool sqlIdentity;
public int sqlIdentitySeed;
public int sqlIdentityIncrement;
public dc_DataColumnDefinition()
{
}
public dc_DataColumnDefinition(string columnName,
System.Type columnType,
string sqlType)
{
this.primaryKey = false;
this.columnName = columnName;
this.columnType = columnType;
this.sqlType = sqlType;
this.sqlNullIsAllowed = true;
this.sqlIdentity = false;
this.sqlIdentitySeed = 0;
this.sqlIdentityIncrement = 0;
}
public dc_DataColumnDefinition(string columnName,
System.Type columnType,
string sqlType, bool sqlNullIsAllowed)
{
this.primaryKey = false;
this.columnName = columnName;
this.columnType = columnType;
this.sqlType = sqlType;
this.sqlNullIsAllowed = sqlNullIsAllowed;
this.sqlIdentity = false;
this.sqlIdentitySeed = 0;
this.sqlIdentityIncrement = 0;
}
public dc_DataColumnDefinition(bool primaryKey, string columnName,
System.Type columnType, string sqlType)
{
this.primaryKey = primaryKey;
this.columnName = columnName;
this.columnType = columnType;
this.sqlType = sqlType;
this.sqlNullIsAllowed = false;
this.sqlIdentity = false;
this.sqlIdentitySeed = 0;
this.sqlIdentityIncrement = 0;
}
public dc_DataColumnDefinition(bool primaryKey,
string columnName, System.Type columnType,
string sqlType, bool sqlIdentity,
int sqlIdentitySeed, int sqlIdentityIncrement)
{
this.primaryKey = primaryKey;
this.columnName = columnName;
this.columnType = columnType;
this.sqlType = sqlType;
this.sqlNullIsAllowed = false;
this.sqlIdentity = sqlIdentity;
this.sqlIdentitySeed = sqlIdentitySeed;
this.sqlIdentityIncrement = sqlIdentityIncrement;
}
}
}
History
- 20th April, 2009: Initial post
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.