this is my code right now:
private static MySqlConnection conn = null;
private static MySqlDataAdapter AccountsDa = null;
private static MySqlCommandBuilder AccountsCb = null;
AccountsDa = new MySqlDataAdapter("SELECT * FROM accounts", conn);
AccountsCb = new MySqlCommandBuilder(AccountsDa);
Accounts = new DataTable();
AccountsDa.Fill(Accounts);
I'm trying to figure out how to define the column default values without having to do it by hand
if I do like this:
DataColumn col = new DataColumn();
col.ColumnName = "id";
col.AllowDBNull = false;
col.DataType = System.Type.GetType("System.Int32");
col.DefaultValue = 0;
Accounts.Columns.Add(col);
for every colum it works fine but how do I have it automatically set the default values from the database when the table is filled. I'm hoping I don't have to define 30 columns by hand.
I tried the Accountsda.FillSchema(Accounts, SchemaType.Source);
which sets up the allow nulls and auto increments but not default values
the problem arrises when adding a row to the data table later sometimes I only need to set the value for one column and let the rest of the columns resort to their default value.
I could put 180 lines of code to manually define the default values for inserting rows but there has to be a way to grab that from the database when creating/filling the data table
I'm using in memory data tables because there are times where data will only exist for example 2 minutes and then be deleted again as this is for a dedicated server for an online rts game. so to save hits on the database I'm using data tables and manipulating them and flushing them every 10 minutes so that I only have 1,000 hits to the database every 10 mins instead of possibly 40,000 hits
well according to the msdn gurus after finally getting a response on their forums its not possible to get the default values. all you can do is load wether the value is allowed to be null and wether its autoincrememnt but then you stll have to set the seed and step on auto incrememnt it doesn't get that from the database either but they gave a shorthand version that cuts it down to 30 lines of code instead of 180
after calling fillschema and then filling the data table can simply do like this wich cuts it down to one line instead of the six
Cities.Columns["wood"].DefaultValue = 0;
after a few replies there is even a much easier way to do this not the way I wanted but maybe it will help someone else down the same road instead of one line for each column this does them all in 3 lines
foreach (DataColumn col in Cities.Columns) {
if (col.ColumnName != "id") col.DefaultValue = 0;
}
id is the primary key and can't set a default value
So I was trying to do something similar to you (except I have no idea how to get the information about auto increment) - I got the idea from https://stackoverflow.com/a/12731310/222897
private void AssignMandatoryColumns([NotNull] DataTable structure, string tableName)
{
// find schema
string[] restrictions = new string[4]; // Catalog, Owner, Table, Column
restrictions[2] = tableName;
DataTable schemaTable = _dbCon.GetSchema("Columns", restrictions);
if (schemaTable == null) return;
// set values for columns
foreach (DataRow row in schemaTable.Rows)
{
string columnName = row["COLUMN_NAME"].ToString();
if (!structure.Columns.Contains(columnName)) continue;
if (row["IS_NULLABLE"].ToString() == "NO") structure.Columns[columnName].AllowDBNull = false;
//if (structure.Columns[columnName].AutoIncrement) continue; // there can be no default value
var valueType = row["DATA_TYPE"];
var defaultValue = row["COLUMN_DEFAULT"];
try
{
structure.Columns[columnName].DefaultValue = defaultValue;
if (!structure.Columns[columnName].AllowDBNull && structure.Columns[columnName].DefaultValue is DBNull)
{
Logger.DebugLog("Database column {0} is not allowed to be null, yet there is no default value.", columnName);
}
}
catch (Exception exception)
{
if (structure.Columns[columnName].AllowDBNull) continue; // defaultvalue is irrelevant since value is allowed to be null
Logger.LogWithoutTrace(exception, string.Format("Setting DefaultValue for {0} of type {1} {4} to {2} ({3}).", columnName, valueType, defaultValue, defaultValue.GetType(), structure.Columns[columnName].AllowDBNull ? "NULL" : "NOT NULL"));
}
}
}
The function takes the DataTable you want to set the values for (I get mine by querying the DB) and the name of the table.
For some reason the timestamp and date columns don't like their default value no matter what I do.
Related
I have a .csv file with around 200 columns and the order of columns changes all the time. I want to read each row from the file, identify the corresponding column names in the database, and write data to the table accordingly.
For this I can use a simple switch case checking for the name of column. Since there are 200 columns, I'm wondering if there is any other way to do it.
Example:
public void ColName(string str, Type a)
{
SampleTableName obj = new SampleTableName();
obj."str" = a;
connection.AddSampleTableName(obj);
connection.savechanges();
}
/* SampleTableName has columns: [Name, Age] */
ColName("Name","XYZ");
Output:
Name Age
XYZ NULL
Any ideas please? Thanks.
If the column names are the same you can use SqlBulkCopy and add a list of Column Mappings. The order doesn't matter, as long as the DataTable name is set.
DataTable table = CreateTable(rows);
using (var bulkCopy = new SqlBulkCopy(connectionString))
{
foreach (var col in table.Columns.OfType<DataColumn>())
{
bulkCopy.ColumnMappings.Add(
new SqlBulkCopyColumnMapping(col.ColumnName, col.ColumnName));
}
bulkCopy.BulkCopyTimeout = 600; // in seconds
bulkCopy.DestinationTableName = "<tableName>";
bulkCopy.WriteToServer(table);
}
If the column names are no the same, a dictionary to lookup the different names could be used.
To keep it simple for maintenance purpose, I went with a switch case sigh. However, I wrote a small script to add all those fields values to the table object.
We have number of store procedures against each data layer. For an example , we have an Employee table with 20 columns and there are about seven store procedures where this table has been referenced. We have one data binding method used against all employee store procedures. Every time i add a new column in the table, i have to add the column reference to all seven store procedure (even though it is not required in all of them). which is bit pain.
As we are using one data binding method, what would be the best way to make this process more efficient?
What if i add a column reference to just in those sp where it is required and then check during data binding if column exists in the dataReader. I don't want to loop through each row and then loop through all columns to find out if column exists. If i have 1000 rows and 20 columns then it would be a loop of 1000 x 20 which is not very efficient.
Would that be okay if i add dataReader results in ArrayList and then use contain method to find if column exists in the ArrayList?
Here's an extension method I found a while back to check for column existence:
Should note that it's not very efficient.
public static bool HasColumn(this IDataRecord dr, string columnName)
{
for (int i = 0; i < dr.FieldCount; i++)
{
if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
}
return false;
}
Perhaps you could use it on the first record and cache the results via some boolean values.
Something like the following:
public void test()
{
//DataBrokerSql is my own helper.
using (DataBrokerSql db = new DataBrokerSql(m_ConnString))
{
bool columnsChecked = false;
bool hasFirstName = false;
bool hasLastName = false;
using (DbDataReader reader = db.GetDataReader("Select * From Person"))
{
while (reader.Read())
{
//Only check for columns on the first row.
if (!columnsChecked)
{
hasFirstName = reader.HasColumn("FirstName");
hasLastName = reader.HasColumn("LastName");
columnsChecked = true;
}
if (hasFirstName)
{
//Read FirstName
var firstName = reader["FirstName"];
}
if (hasLastName)
{
//Read LastName
var lastName = reader["LastName"];
}
}
}
}
}
I have a DataTable dt with 2 columns. First col (call it CustomerId) is unique and doesn't allow nulls. the second one allows nulls and is not unique.
From a method I get a CustomerId and then I would like to either insert a new record if this CustomerId doesn't exist or increment by 1 what's in the second column corresponding to that CustomerId if it exists.
I'm not sure how I should approach this. I wrote a select statement (which returns System.Data.DataRow) but I don't know how to test whether it returned an empty string.
Currently I have:
//I want to insert a new row
if (dt.Select("CustomerId ='" + customerId + "'") == null) //Always true :|
{
DataRow dr = dt.NewRow();
dr["CustomerId"] = customerId;
}
If the datatable is being populated by a database. I would recommend making the customerid a identity column. That way when you add a new row it will automatically create a new customerid which will be unique and 1 greater than the previous id (depending on how you setup your identity column)
I would check the row count which is returned from the select statement. Something like
I would also use string.Format...
So it would look like this
var selectStatement = string.Format("CustomerId = {0}", customerId);
var rows = dt.Select(selectStatement);
if (rows.Count < 1){
var dr = dt.NewRow();
dr["CustomerId"] = customerId;
}
This is my method to solve similar problem. You can modify it to fit your needs.
public static bool ImportRowIfNotExists(DataTable dataTable, DataRow dataRow, string keyColumnName)
{
string selectStatement = string.Format("{0} = '{1}'", keyColumnName, dataRow[keyColumnName]);
DataRow[] rows = dataTable.Select(selectStatement);
if (rows.Length == 0)
{
dataTable.ImportRow(dataRow);
return true;
}
else
{
return false;
}
}
The Select Method returns an array of DataRow objects. Just check if its length is zero (it's never null).
By the way, don't write such statements in the code directly as in this example. There's a technique for breaching your code's security called "SQL Injection", I encourage you to read the Wikipedia Article. In brief, an experienced user could write SQL script that gets executed by your database and potentially do harmful things if you're taking customerId from the user as a string. I'm not experienced in database programming, this is just "general knowledge"...
i was doing some work on a datatable i filled with a oledbdataadapter made from an access database. and i stumbled upon this error:
Turns out that my table has this structure:
ID --> autonumber(PK)
lazos_>text
Asociaciones->text
and when i fill my datatable all values pass to it without any problems with all the correct values. I insert a new row like shown on the "insert row" part.
i do this asumming that my pk will instert the "autonumber" on row creation, but apparently it is not doing it because when i loop trought the rows i get a "invalid cast exception" with a Object cannot be cast from DBNull to other types."
I COULD insert an id value to the column, but when i update my dt to my database wont it create an error, because i have no way of knowing wich was the last row created?, or do i?
for example lets say in my datatable the last ID is 50, but on the database y previously made a record with id "51" but then erased it, if i inserted 51 based on my dt info, it would give an error right?
//// INSERT ROW
DataRow newRow = Tabla_Cods_Proy.NewRow();
newRow["Lazos"] = textBox1.Text ;
newRow["Asociaciones"] = textBox2.Text;
Tabla_Cods_Proy.Rows.Add(newRow);
MessageBox.Show("Enhorabuena!");
//CHECK ID's
for (int i = 0; i < Tabla_Cods_Proy.Rows.Count; i++)
{
if (Tabla_Cods_Proy.Rows[i].RowState != DataRowState.Deleted)
{
if (Tabla_Cods_Proy.Rows[i]["Lazos_asociados"].ToString() == "")
{
listBox7.Items.Add(Tabla_Cods_Proy.Rows[i]["Cod_Cliente"]);
listBox8.Items.Add(Tabla_Cods_Proy.Rows[i]["Cod_Inelectra"]);
ID_Cods_Proy_Sin_Asociar.Add(Convert.ToInt32(Tabla_Cods_Proy.Rows[i]["ID"]));
}
else
{
listBox3.Items.Add(Tabla_Cods_Proy.Rows[i]["Cod_Cliente"]);
listBox4.Items.Add(Tabla_Cods_Proy.Rows[i]["Cod_Inelectra"]);
ID_Cods_Proy_Asociados.Add(Convert.ToInt32(Tabla_Cods_Proy.Rows[i]["ID"]));
}
}
I had once similiar problem. What you need to do is that you retrieve the new identity ##IDENTITY of this column once you insert it into table. You can do that by using RowUpdated event.
Here is quick example from MSDN page (similiar to your case, see bottom of the page):
public static void Main()
{
//...connecting to access db and getting data to datatable...
// ...
// Adding a new row to datatable.
DataRow newRow = catDS.Tables["Categories"].NewRow();
newRow["CategoryName"] = "New Category";
catDS.Tables["Categories"].Rows.Add(newRow);
// Include an event to fill in the Autonumber value.
catDA.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);
}
protected static void OnRowUpdated(object sender, OleDbRowUpdatedEventArgs args)
{
// Include a variable and a command to retrieve the identity value from the Access database.
int newID = 0;
OleDbCommand idCMD = new OleDbCommand("SELECT ##IDENTITY", nwindConn);
if (args.StatementType == StatementType.Insert)
{
// Retrieve the identity value and store it in the CategoryID column.
newID = (int)idCMD.ExecuteScalar();
args.Row["CategoryID"] = newID;
}
}
I have some Excel file reading code that uses the OLEDB (Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=Excel 8.0;) which works well but I keep encountering an issue whereby certain dates are returned as DBNull.
In the original XLS document, the format of dates that work (en-GB locale) are:
"02/04/2009 17:00:00" // returned as a System.DateTime
And the following style fails:
"08/Jan/09 11:24 AM" // returned as DBNull
Excel knows they're both dates (although I can't force them to style correctly) as the following correctly shows a date:
=DATE(YEAR(c),MONTH(c),DAY(c)) // where c = cell reference.
Is there a way, without altering the auto-generated original, to get the data?
EDIT for reference, here is my read-data method (assuming a dbAdapter is set up already -- note the DBNull doesn't come from the catch which isn't fired at all):
public List<List<string>> GetData(string tableName, int maxColumns)
{
List<List<string>> rows = new List<List<string>>();
DataSet ExcelDataSet = new DataSet();
dbCommand.CommandText = #"SELECT * FROM [" + tableName + "]";
dbAdapter.Fill(ExcelDataSet);
DataTable table = ExcelDataSet.Tables[0];
foreach (DataRow row in table.Rows)
{
List<string> data = new List<string>();
for (int column = 0; column < maxColumns; column++)
{
try
{
data.Add(row[column].ToString());
}
catch (Exception)
{
data.Add(null);
}
}
// Stop processing at first blank row
if ( string.IsNullOrEmpty(data[0]) ) break;
rows.Add(data);
}
return rows;
}
I don't know if this will be helpful or not, but I have run into issues with Excel OLEDB code returning NULLs where I expected data and it almost always came back to a data type inference issue. Excel determines the datatype of a column based on the first x rows of data (I think x=10, could be wrong). I know you don't want to alter the file, but it might be worth trying to put the problem date style in the first 10 rows and see if it alters the behavior of your application.
Obviously if it does fix it, then that doesn't solve your problem. The only fixes in that case that I know of are to alter the file (put something in the first 10 rows that forces it to use the correct datatype). Sorry I can't offer a better solution, but hopefully at least I am helping you figure out what's causing your issue.