Set a specific column in a DataRow
-
06-07-2021 - |
Question
I'm trying to perform a "bulk" insert a set of rules into a table of rules. The table has the following structure:
RuleId int,
Rule nvarchar(256),
Domain nvarchar(256),
RuleTypeId int,
ExpirationDate DateTime
In my insertion function I try to set the columns of a row after I've obtained the schema of the table, but it appears that the row does not contain the specified column (note that I don't specify the RuleId since it's auto-generated):
private void InsertRulesXml(List<Rule> rules)
{
using (DataSet ds = new DataSet())
{
using (DataTable rulesTable = GetSchemeForTable("RulesTable"))
{
// Add the rules to the table
foreach (Rule rule in rules)
{
DataRow row = rulesTable.NewRow();
// When I try to set the specific column it tells me that the column doesn't exist
row["Rule"] = rule.Rule;
row["Domain"] = rule.Domain;
row["RuleTypeId"] = rule.RuleTypeId;
row["ExpirationDate"] = rule.Expiration;
rulesTable.Rows.Add(row);
}
ds.Tables.Add(rulesTable);
// Convert the data to XML
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
rulesTable.WriteXml(sw, System.Data.XmlWriteMode.WriteSchema);
}
// Insert the XML rules
InsertUpdateRulesXml(sb.ToString());
}
}
}
Here is the function that gets the schema for the specified table:
private DataTable GetSchemeForTable(string tableName)
{
DataTable ret = null;
using (SqlConnection connection = GetContentScraperSqlConnection())
{
try
{
connection.Open();
SqlCommand command = connection.CreateCommand();
command.CommandText = string.Format("SELECT TOP 0 [{0}].* FROM [{0}] WHERE 1 = 2;", tableName);
using (IDataReader reader = command.ExecuteReader(CommandBehavior.SchemaOnly))
{
ret = reader.GetSchemaTable();
}
}
catch (Exception ex)
{
Console.WriteLine("Exception in GetSchemeForTable: " + ex.ToString());
}
finally
{
if (connection.State == ConnectionState.Open)
{
connection.Close();
}
connection.Dispose();
}
}
return ret;
}
Problem: When I try to set the specific column it tells me that the column doesn't exist.
Exception Message:
An unhandled exception of type 'System.ArgumentException' occurred in System.Data.dll
Additional information: Column 'Rule' does not belong to table SchemaTable.
I suspect that the getting the table schema does not actually give me Rule
table, but the SchemaTable
. What I really want to get is the DataTable which corresponds to the Rule
table (i.e. it has the same schema).
Question: What's the proper way to set the column values in a row given a list of records?
Solution
GetSchemaTable
does not do what it looks like you're expecting it to do. It returns a DataTable
where the rows are the columns of the source table and the columns are metadata about those columns.
If you want to get an "empty" DataTable
with the schema of the given table, change your function to:
private DataTable GetSchemeForTable(string tableName)
{
DataTable ret = new DataTable();
try
{
connection.Open();
SqlCommand command = connection.CreateCommand();
command.CommandText = string.Format("SELECT * FROM [{0}] WHERE 1 = 2;", tableName);
using (IDataReader reader = command.ExecuteReader(CommandBehavior.SchemaOnly))
{
ret.Load(reader);
}
}
catch (Exception ex)
{
Console.WriteLine("Exception in GetSchemeForTable: " + ex.ToString());
}
finally
{
if (connection.State == ConnectionState.Open)
{
connection.Close();
}
connection.Dispose();
}
return ret;
}
notice I took out the using
block since you're disposing of the connection in your finally block anyways (which is essentially what using
does).
OTHER TIPS
Maybe you can use something like this to generate your table:
GenerateDataTable(typeof(Rule));
private DataTable GenerateRulesDataTable(Type type)
{
DataTable table = new DataTable();
// build table columns
foreach (PropertyInfo propInfo in type.GetProperties())
{
Type colType = Nullable.GetUnderlyingType(propInfo.PropertyType) ?? propInfo.PropertyType;
DataColumn col = new DataColumn(propInfo.Name, colType);
table.Columns.Add(col);
}
return table;
}
GetSchemaTable doesn't return DataTable with structure identical as one returned by DataReader select, it returs a table with same schema (ColumnName, ColumnSize, etc) and your schema is in DataTable rows.
In your case you should get DataTable with this select and then you will get empty RulesTable. That way you will be able to insert new rows into a returned DataTable (rulesTable in your case) and do a Update back to database.