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;
}
}
Related
I am trying to make a cart and when user select row in product table, enter quantity and click "add to cart" the row will go to the cart table. i can do it but the image column shows the system.byte insread of the image. also when i select new product and add new it to cart, the previous row in cart table got overwritten instead of adding the new one.
public partial class AddToCartForm : Form
{
public AddToCartForm()
{
InitializeComponent();
}
private void AddToCartForm_Load(object sender, EventArgs e)
{
PopulateProductImageDgv("Select * from ProductDetailsTwo", ref dataGridView1);
dataGridView1.MultiSelect = false;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView2.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dataGridView2.RowTemplate.Height = 100;
dataGridView2.AllowUserToAddRows = false;
}
private void btnSaveToCart_Click(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count > 0)
{
DataTable dt = new DataTable();
dt.Columns.Add("Id");
dt.Columns.Add("Name");
dt.Columns.Add("ImageData");
dt.Columns.Add("Qty");
foreach (DataGridViewRow dgvRow in dataGridView1.SelectedRows)
{
dt.Rows.Add(dgvRow.Cells[0].Value, dgvRow.Cells[1].Value, dgvRow.Cells[2].Value, txtqty.Text.ToString());
}
dataGridView2.DataSource = dt;
}
else
{
MessageBox.Show("select something");
}
}
public void PopulateProductImageDgv(string sql, ref DataGridView dg)
{
using (SqlConnection connection = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["cn"].ConnectionString))
{
connection.Open();
using (SqlCommand cmd = new SqlCommand(sql, connection))
{
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable table = new DataTable();
//settings for dgv with image
dg.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dg.RowTemplate.Height = 100;
dg.AllowUserToAddRows = false;
da.Fill(table);
dg.DataSource = table;
DataGridViewImageColumn imageColumn = new DataGridViewImageColumn();
imageColumn = (DataGridViewImageColumn)dg.Columns[2];
imageColumn.ImageLayout = DataGridViewImageCellLayout.Stretch;
connection.Close();
}
}
}
}
here is the image. any help is appreaciated thanks
There are a few questions I have, however, to keep it simple, one issue is it appears unnecessary to “recreate” a new DataTable for the second (selected items) grid every time the user clicks the save to cart button.
In addition, the posted code simply “overwrites” the existing data in the “selected” items grid. This is an odd behavior. Example, the user clicks an item, then, clicks the save to cart button. This will save the item in the selected items grid, then the user clicks a different item, then clicks the save button… THEN, using the posted, the previously saved item will go away. I am guessing you do not want this behavior.
Given the above comments and the limited posted code, I suggest a simple solution using two different DataTables for this. One table keeps “all” the items and is used for the user to select from. It obviously starts out populated with all the items. The second table is an “empty” table that is used to hold the items selected by the user.
We could obviously “manually” create the selected items table, however, that requires more work and we would need to make sure that certain columns are maintained. "Cloning" the first table will help finding and copying rows from one table to another. In other words, we want to be able to look in the “selected” items table and see if the currently selected item is already in there. If it is, we don’t want to add a new row, we simply want to update the quantity for the existing selected item.
Given this, the second table’s schema could use the same schema as the first table and only display the columns we want, or in this case… “add” a new quantity column. If we Clone the first tables schema then add the “quantity” column to it, then, searching and copying will be much easier. This will work, and you could do this without manually adding the columns to grid. Meaning that even though we added another column to the second table the row import will still succeed.
A complete example is below, the test data uses an original table with columns “ItemID”, “Description” and “Cost” per unit. The second table, also has those columns and two additional columns “QTY” for the quantity and “TotalCost.” The “TotalCost” column is an “Expression” column that simply multiplies the “QTY” value times the “Cost” value. “TotalCost” values will be updated automatically when the “qty” values changes with each button click. First two global data tables for the grids…
DataTable AllItems;
DataTable SelectedItems;
When the form loads, we fill AllItems with all the items from the DB. Then we “clone” this table schema to the SelectedItems table. Then we add the quantity column to the SelectedItems table. And finally set each grid to the proper DataSource. Something like…
private void Form3_Load(object sender, EventArgs e) {
AllItems = GetDataFromDB();
SelectedItems = AllItems.Clone();
DataColumn qtyCol = new DataColumn();
qtyCol.ColumnName = "QTY";
qtyCol.DataType = typeof(int);
SelectedItems.Columns.Add(qtyCol);
DataColumn totCol = new DataColumn();
totCol.ColumnName = "Tot";
totCol.DataType = typeof(decimal);
totCol.Expression = "Cost * QTY";
SelectedItems.Columns.Add(totCol);
//SetSelectedItemsGridColumns();
dataGridView1.DataSource = AllItems;
dataGridView2.DataSource = SelectedItems;
}
The commented out ‘SetSelectedItemsGridColumns` code is used to customize the second grid’s columns if needed.
With this set up, now it should be relatively simple to “copy” the selected rows from the grid with all the items to the grid with the selected items. It should be noted that whatever field we want to search by/find by, must match the field type from the DB. If you get “type” mismatch errors, check to make sure the “type” defined in the code matches the “type” from the DB. In the example below, I use a “type” of int to uniquely identify each “ItemID” in the DB. This will/may obviously be different for you and you will need to change the code to match the proper type/name you want to use.
First, three variables: newItemID to uniquely identify the selected item. And dataRow which is initialized with the data from each selected row. It is used to find the row in the SelectedItems table and also to update existing rows. Lastly a DataRowView to grab the row from the first grid with all the items.
A simple loop through the selected rows. Grab the selected row and get its unique item id. Parse the quantity value from the quantity text box. Try and get the row from the selected items table. If the returned row is null, then the item is not in the table and we need to add it as a new row. If a row IS returned, then we want to simply add the “quantity” value to the existing row.
private void button1_Click(object sender, EventArgs e) {
int newItemID;
DataRow dataRow;
DataRowView drv;
foreach (DataGridViewRow dgr in dataGridView1.SelectedRows) {
drv = (DataRowView)dgr.DataBoundItem;
newItemID = (int)drv["ItemID"];
int.TryParse(txtQTY.Text.Trim(), out int qty);
dataRow = SelectedItems.AsEnumerable().Where(x => x.Field<int>("ItemID") == newItemID).FirstOrDefault();
if (dataRow != null) {
int tot = (int)dataRow["QTY"] + qty;
dataRow["QTY"] = tot;
}
else {
SelectedItems.ImportRow(drv.Row);
dataRow = SelectedItems.AsEnumerable().Where(x => x.Field<int>("ItemID") == newItemID).FirstOrDefault();
dataRow["QTY"] = qty;
}
}
}
private DataTable GetDataFromDB() {
DataTable dt = new DataTable();
dt.Columns.Add("ItemID", typeof(int));
dt.Columns.Add("Description", typeof(string));
dt.Columns.Add("Cost", typeof(decimal));
Random rand = new Random();
for (int i = 1; i < 10; i++) {
dt.Rows.Add(i, "Item_" + i, rand.NextDouble() * 100);
}
return dt;
}
I'm trying to prevent inserting duplicated data into MS Access table like below,
MS Access table(Record) with Columns: Situation, Check_Item, and starts with no data in the table.
DataTable from DataSet is filled with the query "SELECT * FROM Record WHERE Situation = 'A'".
Then I try to do this process,
DataRow = DataTable.Select("Check_Item = '"+InputTextBox.Text"'");
If (DataRow.Length == 0)
{
Use OleDbCommand to insert InputTextBox.Text string to Check_Item of Record table.
}
Result:
First time key in(e.g. 123456), because there is no data in the table, so 123456 is inserted into Record table.
But at the second time key in 123456, then it still be inserted into Record table.
What happened in this process??
Assuming your DataTable variable has name table, and that you created it and linked it correctly to your MS Access database then:
DataRow[] rows = table.Select("Check_Item = '"+InputTextBox.Text"'"); //Selects all rows where the condition is met.
If (rows.Length == 0)
{
//No row met the condition so create a new one and add it to the table.
DataRow newRow = table.NewRow(); //Creates a new row with the same schema (columns) as the table.
newRow["Check_Item"] = InputTextBox.Text; //Assigns the value.
table.Rows.Add(newRow); //Adds the new row to the row collection of the table.
table.AcceptChanges(); //Commits and persist the changes done to the table.
}
Be sure to check this and also the official docs for DataTable.
How can i read the value from every row of a specific column?
I have a table shown here Table Image, i want to get the model name from the "Model" column and i have a database and "SELECT" query behind depending on the "Model" that is shown, it will show the quantity of the "Model" can someone help me?
here is a sample code of my .cs
foreach (GridViewRow row in gv1.Rows)
{
for(int i = 0; i<gv1.Columns.Count;i++)
{
string model = row.Cells[i].Text;
string quan = row.Cells[3].Text;
string ams = row.Cells[4].Text;
}
}
Note:-
data shown in the table are already in the database.
i have 2 seperate View tables for the Models and the Quantity.
If i understand well what you want it's clearly not the best approach, but i will try to help you.
In my opinion you have to get the quantity value before the display of your dataTable, inside this DataTable, but anyway i think this is the type of code that you requested :
//My dataTable to test , with 3 columns
DataTable dt = new DataTable();
dt.Columns.Add("Model");
dt.Columns.Add("Other");
dt.Columns.Add("QuantityFromDB");
//Add some test data in my dataTable
for(int i=0;i<15;i++)
{
DataRow dr = dt.NewRow();
dr[0] = i*2+"";
dr[1] = "XX data XX";
dt.Rows.Add(dr);
}
//Foreach row in your datatable ( your data )
for(int y=0;y<dt.Rows.Count;y++)
{
//Get the value of the current "Model" Column value
string currentModel = dt.Rows[y]["Model"].ToString();
//make your request in the db to get the quantity
int quantityfromdb = 50; //REQUEST
//Update the value of the QuantityFromDB column for this model row
dt.Rows[y]["QuantityFromDB"] = quantityfromdb;
}
You read the value of a specific row, and edit the value of it's other column.
EDIT :
If you want to work directly on the gridview, you can moove your code in the row_databound event of your gridview.
See doc : https://msdn.microsoft.com/fr-fr/library/system.web.ui.webcontrols.gridview.rowdatabound(v=vs.110).aspx
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.
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"...