Question on updating/inserting a datatable in a dataset - c#

I need to open a connection to SQL database and read a subset of a table and either update a record if exists or insert if not found. Having truoble updating
SqlConnection conn = new SqlConnection(ConnectionStrings.PgenIntranet.SqlClientConnectionString);
SqlDataAdapter indicators = new SqlDataAdapter();
string sql = "SELECT * FROM BusinessApplications.tbl_WPI_Site_Indicators where Year = '" + year +
"' and Month = '" + month + "' and PlantId = " + site.ID;
indicators.SelectCommand = new SqlCommand(sql, conn);
SqlCommandBuilder cb = new SqlCommandBuilder(indicators);
indicators.UpdateCommand = cb.GetUpdateCommand();
DataSet ds = new DataSet();
indicators.Fill(ds, "indtable");
DataTable indtable = ds.Tables["indtable"];
// this logic not working
if (indtable.Rows.Count == 0) { indtable.NewRow(); }
DataRow dr = indtable.NewRow();
/// not sure how to make this work
indtable[1]["PlantId"] = site.ID;
dr["PlantId"] = site.ID;

It's been a while since I've used DataSets/DataTables/DataRows, but I think you're close. If I remember correctly, you'll need to create a new row object like you do here:
DataRow dr = indtable.NewRow();
Then populate that row with your data, also similar to how you were doing it:
dr["PlantId"] = site.ID;
Then finally add that row to the rows collection in the DataTable. (You'll want to double-check if your DataTable instance is actually the DataTable in the Tables collection on the DataSet, I don't recall the specifics. It may be safer to reference the Tables collection directly rather than put it in its own object.)
Note that this does not add the row back to the database. It just adds it to the DataTable instance. You'll need to update back to the database accordingly. And this setup that you have here is a bit... messy... so I have no quick answer for that. Since you're just using plain old ADO then I guess you'll need to create an update command and populate it and run it against the connection accordingly.
In doing so, please take care to fix SQL injection vulnerabilities like the one you have there :)

Don't forget to add your new row to the table, then call update...
indtable.Rows.Add(dr);
ds.Update();

Related

Error with databound DataGridView Display Textbox's Text

I have a datagridview (dgvSelectedItem)and I want it to display some values from textboxes but I have this error
Rows cannot be programmatically added to the DataGridView's rows collection when the control is data-bound
My code is:
DataTable dt = new DataTable();
string conn = "server=.;uid=sa;pwd=123;database=PharmacyDB;";
Okbtn()
{
SqlDataAdapter da = new SqlDataAdapter("select UnitPrice from ProductDetails where prdctName like '"+txtSelectedName.Text + "'", conn);
da.Fill(dt);
dgvSelectedItem.DataSource = dt;
//this code work but when I add these lines the Error appear
dgvSelectedItem.Rows.Add(txtSelectedName.Text);
dgvSelectedItem.Rows.Add(txtSelectedQnty.Text); }
Thanks in advance
UPDATED Per OP Comment
So you want a user to enter the product name and quantity, then to run a query against the database to retrieve the unit price information. Then you want to display all of that information to your user in a DataGridView control.
Here is what I suggest:
Include the prdctName field in your SELECT clause.
If you want to bind all relevant data to a DataGridView including the Quantity variable and your TotalPrice calculation, then include them in your SELECT statement. When you bind data to the control from an SQL query like this, the result set is mapped to the grid. In other words, if you want information to be displayed when setting the DataSource property then you need to include the information in your SQL result set.
Don't use LIKE to compare for prdctName as it slightly obscures the purpose of your query and instead just use the = operator.
In addition from a table design perspective, I would add a UNIQUE INDEX on the prdctName column if it is indeed unique in your table - which I sense it likely is. This will improve performance of queries like the one you are using.
Here is what your SQL should look like now:
SELECT prdctName, UnitPrice, #Quantity AS Quantity,
UnitPrice * #Quantity AS Total_Price
FROM ProductDetails
WHERE prdctName = #prdctName
A few other suggestions would be to use prepared SQL statements and .NET's using statement as already noted by Soner Gönül. I'll try to give you motivation for applying these techniques.
Why should I use prepared SQL statements?
You gain security against SQL Injection attacks and a performance boost as prepared statements (aka parameterized queries) are compiled and optimized once.
Why should I use the using statement in .NET?
It ensures that the Dispose() method is called on the object in question. This will release the unmanaged resources consumed by the object.
I wrote a little test method exemplifying all of this:
private void BindData(string productName, int quantity)
{
var dataTable = new DataTable();
string sql = "SELECT prdctName, UnitPrice, #Quantity AS Quantity, " +
"UnitPrice * #Quantity AS Total_Price " +
"FROM ProductDetails " +
"WHERE prdctName = #prdctName";
using (var conn = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("#prdctName", productName);
cmd.Parameters.AddWithValue("#Quantity", quantity);
using (var adapter = new SqlDataAdapter(cmd))
{
adapter.Fill(dataTable);
dataGridView1.DataSource = dataTable;
}
}
}
}
Here's a sample input and output of this method:
BindData("Lemon Bars", 3);
I searched over the internet and looks like there is no way to add new rows programmatically when you set your DataSource property of your DataGridView.
Most common way is to add your DataTable these values and then bind it to DataSource property like:
da.Fill(dt);
DataRow dr = dt.NewRow();
dr["UnitPrice"] = txtSelectedName.Text;
dt.Rows.Add(dr);
dt.Rows.Add(dr);
dgvSelectedItem.DataSource = dt;
Also conn is your connection string, not an SqlConnection. Passing SqlConnection as a second parameter in your SqlDataAdapter is a better approach in my opinion.
You should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Finally, don't forget to use using statement to dispose your SqlConnection and SqlDataAdapter.
I assume you want to use your text as %txtSelectedName.Text%, this is my example;
DataTable dt = new DataTable();
using(SqlConnection conn = new SqlConnection("server=.;uid=sa;pwd=123;database=PharmacyDB;"))
using(SqlCommand cmd = new SqlCommand("select UnitPrice from ProductDetails where prdctName like #param"))
{
cmd.Connection = conn;
cmd.Parameter.AddWithValue("#param", "%" + txtSelectedName.Text + "%");
using(SqlDataAdapter da = new SqlDataAdapter(cmd, conn))
{
da.Fill(dt);
DataRow dr = dt.NewRow();
dr["UnitPrice"] = txtSelectedName.Text;
dt.Rows.Add(dr);
dt.Rows.Add(dr);
dgvSelectedItem.DataSource = dt;
}
}

Update using MySqlDataAdapter doesn't work

I am trying to use MySqlDatAdapter to update a MySql table. But, the table never updates!!! I did this before but with SQL server. Is there anything else that is specific to MySql that I am missing in my code?
DataTable myTable = new DataTable("testtable");
MySqlConnection mySqlCon = new MySqlConnection(ConfigurationManager.ConnectionStrings["DBConStr"].ConnectionString);
MySqlCommand mySqlCmd = new MySqlCommand("SELECT * FROM testtable WHERE Name = 'Tom'");
mySqlCmd.Connection = mySqlCon;
MySqlDataAdapter adapter = new MySqlDataAdapter(mySqlCmd);
MySqlCommandBuilder myCB = new MySqlCommandBuilder(adapter);
adapter.UpdateCommand = myCB.GetUpdateCommand();
mySqlCon.Open();
adapter.Fill(myTable);
myTable.Rows[0]["Name"] = "Was Tom";
myTable.AcceptChanges();
adapter.Update(myTable);
mySqlCon.Close();
Thanks
Remove myTable.AcceptChanges() before the update. Othwerwise that will set all rows RowState to Unchanged, hence the DataAdapter will not know that something was changed.
adapter.Update(myTable) will call AcceptChanges itself after the update is finished.
So...
myTable.Rows[0]["Name"] = "Was Tom";
//myTable.AcceptChanges();
adapter.Update(myTable);
My some one need to look into the following solution; In other scenario people may need different solution. Even Don't do any manipulation with Datatable when you Debug at Run-time like this,
myTable.GetChanges(); // Return Any of Chnages Made without applying myTable.Accepchanges()
myTable.GetChanges(DataRowState.Added); // Return added rows without applying myTable.Accepchanges()
myTable.GetChanges(DataRowState.Deleted);
myTable.GetChanges(DataRowState.Detached);
myTable.GetChanges(DataRowState.Modified);
myTable.GetChanges(DataRowState.Unchanged);
You may get Data According to the above commands. So better try to debug before you pass the datatable to update or insert or delete command.
If myTable.GetChanges() return null then you can SetAdded() or SetModified() back to your DataTable;
foreach(DataRow row in myTable.Rows)
{
row.SetAdded(); // For Insert Command
row.SetModified(); // For Update Command
}

local database won't update and doesn't show errors

Hey i'm new to this and from what i managed to pick up this should be working but it doesn't update my local database.
I have a TelemarketingDatabaseDataSet that was auto generated when i created my local database, which then i dragged the table onto the dataset and i guess they're linked.
Now i have this code :
SqlCeConnection connection = new SqlCeConnection();
connection.ConnectionString = TelemarketingTracker.Properties.Settings.Default.TelemarketingDatabaseConnectionString;
TelemarketingDatabaseDataSet ds = new TelemarketingDatabaseDataSet();
// DataTable tbl = new DataTable();
SqlCeDataAdapter adapter = new SqlCeDataAdapter("select * from Calls", connection);
//adapter.InsertCommand = new SqlCeCommand("InsertQuery", connection);
adapter.Fill(ds,"Calls");
DataTable tbl = ds.Tables["Calls"];
//tbl.Columns.Add("caller");
//tbl.Columns.Add("called");
//tbl.Columns.Add("duration");
//tbl.Columns.Add("time");
var row = tbl.NewRow();
row[1] = Convert.ToString(caller);
row[2] = Convert.ToString(called);
row[3] = Convert.ToString(duration);
row[4] = Convert.ToDateTime(time);
tbl.Rows.Add(row);
adapter.Update(ds, "Calls");
connection.Close();
MessageBox.Show("Database should be updated!");
And please, i'm not intrested in using an SqlCommand as i prefer using DataSet.
Could the problem be related to datatypes of my table? it doesn't show errors to suggest that but i guess this could be the problem. my Table consists of :
ID - int,key
caller - varchar
called - varchar
duration - varchar
time - datetime
EDIT:
Now if i uncomment the insertQuery row i get an unhandled error occured in Syste.Data dll.
Now even if i try to use a regular insert command i get no errors but the database won't update.
if this makes any diffrence after i close the debugging window i see an X next to the local database but it doesn't show any errors.
This is the command i've tried :
using (SqlCeCommand com = new SqlCeCommand("INSERT INTO Calls (caller, called, duration, time) Values(#Caller,#Called,#Duration,#Time)", connection))
{
com.Parameters.AddWithValue("#Caller", row[1]);
com.Parameters.AddWithValue("#Called", row[2]);
com.Parameters.AddWithValue("#Duration", row[3]);
com.Parameters.AddWithValue("#Time", row[4]);
com.ExecuteNonQuery();
}
The Fill() method "Adds or refreshes rows in the DataSet to match those in the data source." The key part of this sentence being "to match those in the data source". The row you're adding gets wiped out when you call Fill() because it's not already in the source.
I'm not positive, but I don't think that you need to even call Fill() if you're only adding new records and not worried about modifying/removing existing ones. If you do need to call it though, it would obviously need to be moved before any new record insertions you make.
Try something similar to this..
string dbfile = new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\TelemarketingDatabase.sdf";
SqlCeConnection connection = new SqlCeConnection("datasource=" + dbfile);
TelemarketingDatabaseDataSet ds = new TelemarketingDatabaseDataSet();
SqlCeDataAdapter adapter = new SqlCeDataAdapter();
string qry = #"select * from Calls";
da.SelectCommand = new SqlCommand(qry, connection);
SqlCommandBuilder cb = new SqlCommandBuilder(adapter);
adapter.Fill(ds,"Calls");
DataTable tbl = ds.Tables["Calls"];
var row = tbl.NewRow();
row[0] = caller;
row[1] = called;
row[2] = duration;
row[3] = Convert.ToDateTime(time);
tbl.Rows.Add(row);
adapter.Update(ds,"Calls");
See the example here http://www.java2s.com/Code/CSharp/Database-ADO.net/UseDataTabletoupdatetableinDatabase.htm
Well in the end i didn't manage to solve this, instead i used a remote database and regular sql commands.
Thanks for those who helped!
just want to share this even the if the question is old
using System;
using System.IO; //needed for path.getdirectoryname() and directory.getcurrentdirectory()
string path = Path.GetDirectoryName(Path.GetDirectoryName(Directory.GetCurrentDirectory()));
AppDomain.CurrentDomain.SetData("DataDirectory", path);
Directory.GetcurrentDirectory() will output "C:/..projectname/bin/debug" which is where the temporary database.mdf is located
by using Path.GetDirectoryName(Directory.GetcurrentDirectory()) it will give the directory of the current directory thus moving one location back
"C:/..projectname/bin"
then use it again
Path.GetDirectoryName(Path.GetDirectoryName(Directory.GetCurrentDirectory())) will give you the location of the root database in your project folder
"C:/..projectname"
then just use AppDomain.CurrentDomain.SetData()

Problem with ADO.NET UPDATE code

Could somebody take a quick peek at my ado.net code? I am trying to update the row from a dataset, but it just isn't working. I am missing some elemental piece of the code, and it is just eluding me. I have verified that the DataRow actually has the correct data in it, so the row itself is accurate.
Many thanks in advance.
try
{
//basic ado.net objects
SqlDataAdapter dbAdapter = null;
DataSet returnDS2 = new DataSet();
//a new sql connection
SqlConnection myConn = new SqlConnection();
myConn.ConnectionString = "Server=myserver.mydomain.com;"
+ "Database=mydatabase;"
+ "User ID=myuserid;"
+ "Password=mypassword;"
+ "Trusted_Connection=True;";
//the sqlQuery
string sqlQuery = "select * from AVLUpdateMessages WHERE ID = 21";
//another ado.net object for the command
SqlCommand cmd = new SqlCommand();
cmd.Connection = myConn;
cmd.CommandText = sqlQuery;
//open the connection, execute the SQL statement and then close the connection.
myConn.Open();
//instantiate and fill the sqldataadapter
dbAdapter = new SqlDataAdapter(cmd);
dbAdapter.Fill(returnDS2, #"AVLUpdateMessages");
//loop through all of the rows; I have verified that the rows are correct and returns the correct data from the db
for (int i = 0; i <= returnDS2.Tables[0].Rows.Count - 1; i++)
{
DataRow row = returnDS2.Tables[0].Rows[i];
row.BeginEdit();
row["UpdatedText"] = #"This is a test...";
row.EndEdit();
}
//let's accept the changes
dbAdapter.Update(returnDS2, "AVLUpdateMessages");
returnDS2.AcceptChanges();
myConn.Close();
}
I think you need an update query in your data adapter. I know, this sucks... Alternatively you can use CommandBuilder class to automatically generate queries for CRUD operations.
example at: http://www.programmersheaven.com/2/FAQ-ADONET-CommandBuilder-Prepare-Dataset
You might be able to use SqlCommandBuilder to help out. After the Fill call, add the following statement. That will associate a command builder with the data adapter and (if there is a primary key available) it should generate the update statement for you. Note that there is some expense behind the command builder. It may not be much relative to everything else, but it does involve looking at schema information (to get primary key information, field names, field types, etc.) for the table and generating INSERT, DELETE, and UPDATE statements involving all fields in the table.
SqlCommandBuilder cb = new SqlCommandBuilder(dbAdapter);
Wait, why not something like
update AVLUpdateMessages set UpdatedText = 'This is a test...' where id = 21
If you're picking through all the rows of a table to update one at a time, you're probably doing it wrong. SQL is your friend.

Using OleDbDataAdapter and DataSet to update Access.mdb

I am attempting to update a simple ms access database. I get an Exception on certain tables that, after searching, I found Microsoft Support - Syntax Error. I believe it means that one of the column names uses a reserved word. This seems to be the case, since all the tables update except the ones with "GUID" as one of the column names, a reserved word. This page also states that I should be using a OleDbAdapter and DataSet, which should solve the problem. Unfortunately I cannot change the name of the column. That is beyond my control, so I have to work with what is given me.
I haven't had to do work with databases much, and everything I know I've learned from examples from the internet (probably bad ones at that). So what is the proper way to update a database using OleDbAdapter and dataSet?
I don't think I should be using DataTable or OleDbCommandBuilder, and I believe the solution has something to do with parameters. But my googleing skills are weak.
OleDbConnection conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; " +
Data Souce=" + source);
conn.Open();
OleDbAdapter adapter = new OleDbDataAdapter("SELECT * From " + table, conn);
OleDbCommandBuiler cmdBuiler = new OleDbCommandBuilder(adapter);
DataSet = new DatSet();
adapter.InsertCommand = cmdBuilder.GetInertCommand(true); // Is this necessary?
adapter.Fill( dataSet, table);
DataTable dataTable = dataSet.Tables[table]; // Do I need a DataTable?
DataRow row = dataTable.
row [ attribute ] = field; // Do this for all attributes/fields. I think this is wrong.
dataTable.rows.Add(row);
adapter.Update(dataTable); //<--"Syntax error in INSERT INTO statement." Exception
The problem may be that the column names (especially those whose name are reserved words) should be surrounded by square brackets. The OleDbCommandBuilder, when it creates its own InsertCommand, doesn't surround the names with brackets, so a solution is to manually define the OleDbDataAdapter's InsertCommand:
adapter.InsertCommand = new OleDbCommand(String.Format("INSERT INTO {0} ([GUID], [fieldName]) Values (#guid,#fieldName);", table), conn);
Defining parameters for each column and then manually adding the parameter's values;
adapter.InsertCommand.Parameters.Add(new OleDbParameter("#guid",row["GUID"]));
So summing up, for the tables which have a column named "GUID", you should try something like the following:
OleDbConnection conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" +
"Data Souce=" + source);
conn.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * From " + table, conn);
OleDbCommandBuilder cmdBuilder = new OleDbCommandBuilder(adapter);
adapter.InsertCommand = new OleDbCommand(String.Format("INSERT INTO {0} ([GUID], [fieldName]) Values (#guid,#fieldName);", table), conn);
DataTable dataTable = new DataTable(table);
adapter.Fill( dataTable);
DataRow row = dataTable.NewRow();
row [ fieldName ] = fieldValue;
// eg: row [ "GUID" ] = System.Guid.NewGuid().ToString(); // Do this for all attributes/fields.
dataTable.Rows.Add(row);
adapter.InsertCommand.Parameters.Add(new OleDbParameter("#fieldName",row[fieldName]));
// eg: adapter.InsertCommand.Parameters.Add(new OleDbParameter("#guid",row["GUID"]));
adapter.Update(dataTable);
As to problem #1. Try doing a full qualification of the column name i.e. table.columnName (that fixes the problem in MySQL so maybe it does in Access) also, try putting [ ] around the column name.
Select * is usually a poor option to specifying the column names and using aliases. For example use Select Column1 as 'Column1', Column2 as 'Column2' ....
this makes working with your dataset and datatable much easier as you can access the column by its alias instead of by column indexes.
I find that the DataAdapter is much more useful for filling datasets than for actually modifying a database. I recommend something like:
string updateQuery = "Update ..... Where ...."; //do your magic here
OldDbcommand command = new OleDbCommand(updateQuery);
command.Connection = conn;
conn.Open();
con.ExecuteNonQuery();
conn.Close();
You could fill your dataset with the adapter and then do as I just did to execute your update commands on the DB.
A good place to start would be using DataSetDesigner and Typed DataSets to start
try this walk through : http://msdn.microsoft.com/en-us/library/ms171893(v=vs.80).aspx
A good longterm approach is to use Sql Server Express instead, then you'll have a choice of using : Entity Framework, Linq To Sql or Still keep using the DataSetDesigner and Typed DataSets.

Categories