Updating SQLite DB from DGV (C#) - c#

On one form I have a dgv. From another form, I can add an item to the dgv and also place the new item into the SQLite database.
What I'm trying to do is also be able to edit the item from the dgv, and have the edit be saved in the database also.
I have this code for CellEndEdit event:
SetConnection();
sqlconnection.Open();
this.dataGridView1.Rows[e.RowIndex].Selected = true;
this.rowIndex1 = e.RowIndex;
this.dataGridView1.CurrentCell = this.dataGridView1.Rows[e.RowIndex].Cells[0];
sqlcmd = new SQLiteCommand("UPDATE table1 SET item = #item, quantity = #quantity WHERE id= " + this.dataGridView1.Rows[this.rowIndex1].Cells["id"].Value, sqlconnection);
sqlcmd.Parameters.AddWithValue("#item", this.dataGridView1.Rows[this.rowIndex1].Cells["item1"].Value);
sqlcmd.Parameters.AddWithValue("#quantity", this.dataGridView1.Rows[this.rowIndex1].Cells["quantity1"].Value);
sqlcmd.ExecuteNonQuery();
sqlconnection.Close();
This code works, but only if I load the database to the dgv.
When the program is first opened, the database isn't loaded into the dgv. The problem I run into, is when I add a new item (and its the only item present in the dgv), and I try to edit it (aka. change name.etc.), I get the following error: SQL logic error or missing database
near " ": syntax error
Note: When the dgv is empty and I add a new item, the new item is successfully added to the database table.
Also Note: 'id' is the PRIMARY KEY and AUTOINCREMENTed

The situation you're having here is that when you add a new item to the DGV, you are not providing a value to the ID column. So at the end of the query
"UPDATE table1 SET item = #item, quantity = #quantity WHERE id= " + this.dataGridView1.Rows[this.rowIndex1].Cells["id"].Value
This will become like id = . because the ID column in the DGV is currently empty and is definitely a Syntax Error.
It works when you load data because, you are filling up this column. So the solution is to provide value to the ID column properly when you insert a new item.
After inserting an entry to the db, get the Automatically Incremented ID by using a query
Select last_insert_rowid();
Read the value using reader and apply it to the ID column of the table

This works for me.
private void btnUpdate_Click(object sender, EventArgs e)
{
using (SqlConnection con = new SqlConnection("Server=your_server_name;Database=your_db_name;Trusted_Connection=True;"))
{
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Courses", con))
{
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
{
SqlCommandBuilder sqlcmd = new SqlCommandBuilder(da);
DataSet ds = new System.Data.DataSet(); // remove this line
da.Update(this.ds, "Courses");
}
}
}
}
}

Related

Update single cell in datagrid WPF

I have a datagrid in my WPF application. In one column I have an int (column name = Amount).
So for example there will be a number "4" in the cell. I can edit "4" in the DataGrid to "3".
After editing, I will push the button "Update" so my database column Amount will be updated.
It is working, but it update all the cells in the column Amount to the Id number of the chosen row.
This is my code in the xaml.cs file:
private void Update(object sender, RoutedEventArgs e)
{
DataRowView o = (DataRowView)g2.SelectedItem;
int Amount = Convert.ToInt32(o.Row.ItemArray[0]);
try
{
const string query = #"UPDATE [Stock] SET [STOCK].Amount = #Aantal;";
using (SqlConnection con = new SqlConnection("Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename=\"...."))
using (SqlCommand cmd = new SqlCommand(query, con))
{
cmd.Parameters.Add("#Amount", SqlDbType.Int).Value = Amount;
con.Open();
cmd.ExecuteNonQuery();
}
MessageBox.Show("Update complete");
binddatagrid();
}
catch (Exception ex)
{
MessageBox.Show("Error occurred:\r\n" + ex.Message);
}
}
What am I doing wrong?
Your database query is updating the Amount column in every row in the [Stock] table. You need to add a WHERE clause to your database query so that you only update the [Stock] row in the database that corresponds to the selected row in the DataGrid.
I don't know what your database schema looks like, but I'm assuming that the [Stock] table has an Id column. If so, the query might look something like this:
UPDATE [Stock] SET [Stock].Amount = #Anatal WHERE [Stock].Id = #Id
Notice that the query now has a second parameter, #Id. That means that you'll need to get the Id from the selected row in much the same way that you're currently getting the Amount.
int id = Convert.ToInt32(o.Row.ItemArray[1]);
I used o.Row.ItemArray[1], but I don't know what index the Id will actually be stored at. You'll have to use that index to get the correct Id.
Since your query has a second parameter, you also need to add it to the Parameters collection of the SqlCommand instance. Just like how you're doing with Amount.
cmd.Parameters.Add("#Id", SqlDbType.Int).Value = id;

Delete all access database table data

I have an access database that I am manipulating with C#.
I have connected to it, retrieved a data-set from it and can add rows to a table. Now I am trying to clear a table and I am unable to get it to work.
I have tried TRUNCATE TABLE table_name but that throws an exception saying that I must use either DELETE, INSERT, PROCEDURE, SELECT or UPDATE and I have tried Delete FROM table_name However that throws an DBConcurrenceyException.
Here is what I have to tried to clear the table:
private void ClearBut_Click(object sender, EventArgs e)
{
OleDbDataAdapter dtaAdpTestTableClear = new OleDbDataAdapter();
OleDbCommand command;
command = new OleDbCommand("DELETE FROM TestTable", con);
dtaAdpTestTableClear.DeleteCommand = command;
foreach (DataRow row in dsWCSDHDB.Tables["TestTable"].Rows)
{
row.Delete();
}
dtaAdpTestTableClear.Update(dsWCSDHDB.Tables["TestTable"]);
}
My other add method
private void Add_Click(object sender, EventArgs e)
{
OleDbDataAdapter dtaAdpTestTableInsertNewRow = new OleDbDataAdapter();
OleDbCommand command;
// Create the InsertCommand.
// This is needed as DataAdaptor.InsertCommand() is called during the update to insert the row into the database. It requires an insert query
command = new OleDbCommand("INSERT INTO TestTable (id, someData) " +"VALUES (?, ?)", con); //We create a dbcommand the command is, Querytype, what we are doing with it, what table, (columns we are using), concat, Values we will be adding(as ? for now as we will pass this data in latter), connection to the database
command.Parameters.Add("id", OleDbType.Char, 5, "id"); //this is where we add a parameter to the command function. we add one per column in the row (columns we are using name, value type, column length, source column, these parameters will replace the ? in the query above
command.Parameters.Add("someData", OleDbType.VarChar, 40, "someData");
dtaAdpTestTableInsertNewRow.InsertCommand = command;// we attach this command to the Insert command function of the adapter that we are using
//Create the new row
DataRow row = dsWCSDHDB.Tables["TestTable"].NewRow(); //Create a new empty row that is formated for the TestTable table
row["someData"] = AddValueTextBox.Text.ToString();// add in the values
//Add the new row to the dataset table
dsWCSDHDB.Tables["TestTable"].Rows.Add(row); //adds this new row to the clients dataset
//Updates the database table with the values of the clients dataset Table
//For this to work you need to build a proper data adapter that is using a query taylered for the table you are using.
//Unfortunately although it would be nice to be able to add and use tables to the database with out changing the code you cant build a generic one that works for all tables in the database.
//this is because different tables can have different fields and column lengths .
//there is a example of how to build one below
//Update the database table with the values of the clients dataset Table
dtaAdpTestTableInsertNewRow.Update(dsWCSDHDB.Tables["TestTable"]); // using the adapter that we created above we update the database with the clients dataset.
}
You will just need to call ExecuteNonQuery
private void ClearBut_Click(object sender, EventArgs e)
{
string comand = "DELETE FROM TestTable";
OleDbCommand cmd = new OleDbCommand(comand, con);
cmd.ExecuteNonQuery();
}

How to add a new column to datagridview after binding it with database

I have a dataGridView in my winForm (C#) application to display a list of Players.
I have successfully bound it with the database and its showing all columns i.e PlayerName, Age, Runs etc. correctly.
Now i want to add one more columns i.e 'CurrentScore' to this dataGridView at run time without adding to database.
Plz tell how to perform this tast. Is it possible to add new column programmatically ??
private void playerList(int teamID)
{
if (con.State == ConnectionState.Closed)
{
con.Open();
}
string query = "SELECT PlayerName AS [Player Name] ,Age , Runs FROM Players WHERE Team_id= " + teamID;
SqlCommand cmd = new SqlCommand(query, con);
adapter = new SqlDataAdapter(cmd);
adapter.Fill(ds, "Players");
dGridPlayers.DataSource = ds.Tables["Players"];
}
this code is working perfectly but not able to add new temporary column 'CurrentScore`
You can just edit your query and add a dummy column:
string query = "SELECT player_name AS [Player Name] ,
skill As [Skill], '' AS [CurrentScore]
FROM Players WHERE Team_id= " + teamID;

C# Fill combo box from SQL DataTable

DataTable _dt = new DataTable();
using (SqlConnection _cs = new SqlConnection("Data Source=COMNAME; Initial Catalog=DATABASE; Integrated Security=True"))
{
string _query = "SELECT * FROM Doctor";
SqlCommand _cmd = new SqlCommand(_query, _cs);
using (SqlDataAdapter _da = new SqlDataAdapter(_cmd))
{
_da.Fill(_dt);
}
}
cbDoctor.DataSource = _dt;
foreach(DataRow _dr in _dt.Rows)
{
cbDoctor.Items.Add(_dr["name"].ToString());
}
There was an Error...
The result is System.Data.DataRowView instead of data from database..
I'm not yet sure what is the exact error in your code, but if you're ok with not using DataTable, you can do it this way:
using (SqlConnection sqlConnection = new SqlConnection("connstring"))
{
SqlCommand sqlCmd = new SqlCommand("SELECT * FROM Doctor", sqlConnection);
sqlConnection.Open();
SqlDataReader sqlReader = sqlCmd.ExecuteReader();
while (sqlReader.Read())
{
cbDoctor.Items.Add(sqlReader["name"].ToString());
}
sqlReader.Close();
}
For more information take a look at SqlDataReader reference on MSDN.
In orer to find the issue in the original code you posted, please provide information in which line you get the exception (or is it an error that prevents application from compiling?) and what is its whole message.
You could also specify DisplayMember property of combobox to any of the column name.
For example if you want to display Name field, you could do
Combobox1.DisplayMember="Name";
I think the problem is that you are trying to insert multiple columns into a comboBox (which can accept only one column). Adjust your SELECT statement so that it combines all of the data you need into one column:
Example:
SELECT Name + ' ' LastName + ' '+ ID AS 'DoctorData' FROM Doctor
By using the + operator you can combine multiple columns from the database, and represent it as a single piece of data. I think that your code should work after that (you can even comment the foreach loop, I think that adding data source to your comboBox will be enough)

add row to a BindingSource gives different autoincrement value from whats saved into DB

I have a DataGridView that shows list of records and when I hit a insert button, a form should add a new record, edit its values and save it.
I have a BindingSource bound to a DataGridView. I pass is as a parameter to a NEW RECORD form so
// When the form opens it add a new row and de DataGridView display this new record at this time
DataRowView currentRow;
currentRow = (DataRowView) myBindindSource.AddNew();
when user confirm to save it I do a
myBindindSource.EndEdit(); // inside the form
and after the form is disposed the new row is saved and the bindingsorce position is updated to the new row
DataRowView drv = myForm.CurrentRow;
avaliadoTableAdapter.Update(drv.Row);
avaliadoBindingSource.Position = avaliadoBindingSource.Find("ID", drv.Row.ItemArray[0]);
The problem is that this table has a AUTOINCREMENT field and the value saved may not correspond the the value the bindingSource gives in EDIT TIME.
So, when I close and open the DataGridView again the new rowd give its ID based on the available slot in the undelying DB at the momment is was saved and it just ignores the value the BindingSource generated ad EDIT TIME,
Since the value given by the binding source should be used by another table as a foreingKey it make the reference insconsistent.
There's a way to get the real ID was saved to the database?
I come up with this solution
First added a GetNextID() method directly to the table model:
SELECT autoinc_next
FROM information_schema.columns
WHERE (table_name = 'Estagio') AND (column_name = 'ID')
and whener I need a new row to be added I do
EstagioTableAdapter ta = new EstagioTableAdapter ();
nextID = ta.GetNextID();
row = (DataRowView)source.AddNew();
row.Row["ID"] = nextID;
(...)
source.EndEdit();
The same thing happens with Access databases. There is a great article (with solution) here. Basically, the TableAdapter normally sends 2 queries in a batch when you save the data. The first one saves the data and the second one asks for the new ID. Unfortunately, neither Access nor SQL CE support batch statements.
The solution is to add an event handler for RowUpdated that queries the DB for the new ID.
based on my answer on concurrency violation, use da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord.
note: just change SQLiteConnection and SQLiteDataAdapter to MSSQL ones, and change the LAST_INSERT_ROWID() to SCOPE_IDENTITY()
const string devMachine = #"Data Source=C:\_DEVELOPMENT\__.NET\dotNetSnippets\Mine\TestSqlite\test.s3db";
SQLiteConnection c = new SQLiteConnection(devMachine);
SQLiteDataAdapter da = new SQLiteDataAdapter();
DataTable dt = new DataTable();
public Form1()
{
InitializeComponent();
da = new SQLiteDataAdapter("select product_id, product_name, abbrev from product", c);
var b = new SQLiteCommandBuilder(da);
da.InsertCommand = new SQLiteCommand(
#"insert into product(product_id, product_name, abbrev) values(:_product_id, :_product_name, :_abbrev);
select product_id /* include rowversion field here if you need */
from product where product_id = LAST_INSERT_ROWID();", c);
da.InsertCommand.Parameters.Add("_product_id", DbType.Int32,0,"product_id");
da.InsertCommand.Parameters.Add("_product_name", DbType.String, 0, "product_name");
da.InsertCommand.Parameters.Add("_abbrev", DbType.String, 0, "abbrev");
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
da.UpdateCommand = b.GetUpdateCommand();
da.DeleteCommand = b.GetDeleteCommand();
da.Fill(dt);
bds.DataSource = dt;
grd.DataSource = bds;
}
private void uxUpdate_Click(object sender, EventArgs e)
{
da.Update(dt);
}
here's the sample table on SQLite:
CREATE TABLE [product] (
[product_id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[product_name] TEXT NOT NULL,
[abbrev] TEXT NOT NULL
)
[EDIT Nov 19, 2009 12:58 PM CN] Hmm... I guess my answer cannot be used, SQLCE does not allow multiple statements.
anyway, just use my answer when you use server-based MSSQL or if you use SQLite. or perhaps, encapsulate the two statements to a function that returns scope_identity(integer):
da.InsertCommand = new SQLiteCommand(
#"select insert_to_product(:_product_id, :_product_name, :_abbrev) as product_id", c);
da.InsertCommand.Parameters.Add("_product_id", DbType.Int32,0,"product_id");
da.InsertCommand.Parameters.Add("_product_name", DbType.String, 0, "product_name");
da.InsertCommand.Parameters.Add("_abbrev", DbType.String, 0, "abbrev");
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
note: just change SQLiteConnection and SQLiteDataAdapter to MSSQL ones, and change the LAST_INSERT_ROWID() to SCOPE_IDENTITY()
use RowUpdated (shall work on SQLCE and RDBMS that doesn't support multi-statements):
const string devMachine = #"Data Source=C:\_DEVELOPMENT\__.NET\dotNetSnippets\Mine\TestSqlite\test.s3db";
SQLiteConnection c = new SQLiteConnection(devMachine);
SQLiteDataAdapter da = new SQLiteDataAdapter();
DataTable dt = new DataTable();
public Form1()
{
InitializeComponent();
da = new SQLiteDataAdapter("select product_id, product_name, abbrev from product", c);
var b = new SQLiteCommandBuilder(da);
da.InsertCommand = b.GetInsertCommand();
da.UpdateCommand = b.GetUpdateCommand();
da.DeleteCommand = b.GetDeleteCommand();
da.Fill(dt);
da.RowUpdated += da_RowUpdated;
bds.DataSource = dt;
grd.DataSource = bds;
}
void da_RowUpdated(object sender, System.Data.Common.RowUpdatedEventArgs e)
{
if (e.StatementType == StatementType.Insert)
{
int ident = (int)(long) new SQLiteCommand("select last_insert_rowid()", c).ExecuteScalar();
e.Row["product_id"] = ident;
}
}
private void uxUpdate_Click(object sender, EventArgs e)
{
da.Update(dt);
}
I haven't had a chance to use SQLiteConnection class but I do used SQLConnection and SQLCommand class. SqlCommand has a method ExecuteScalar that return the value of the first row and first column of your t-sql statement. You can use it to return the Auto-Identity column. Also, in SQL Server 2005 there is a keyword named OUTPUT you may also check it too.
I've come across this: all you need to do is set your autoincrement seed to -1 and have it "increment" by -1 too. This way all your datarows will have unique ids that DON'T map to anything in the real database. If you're saving your data with a DataAdapter, then after the save your datarow and any other rows with a datarelation pointing to that id will be updated

Categories