I'm trying to read data from a SQLite database. Then I'd like to fill it into my DataGrid, but the following code doesn't work (dataUrl is the DataGrid-Object):
string sql = "SELECT rowid, url FROM urls WHERE url LIKE '%#search%'";
SQLiteConnection myConnection = new SQLiteConnection(#"Data Source=C:\URL Store\URL Store\bin\Release\urlStore.sqlite;Version=3;Password=*censored*;");
SQLiteCommand command = new SQLiteCommand(sql, myConnection);
command.Parameters.AddWithValue("#search", txtSearch.Text);
myConnection.Open();
command.ExecuteNonQuery();
SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
DataSet set = new DataSet();
try
{
//
//ESPECIALLY THIS PART IS IMPORTANT TO ME
//
adapter.Fill(set);
DataTable table = set.Tables[0];
dataUrl.DataContext = table;
}
catch (Exception ex)
{
MessageBox.Show("Error loading data!");
}
It does't even throw an exception.
You should set ItemsSource instead of DataContext and for that you need to get DataView as it only accepts IEnumerable:
dataUrl.ItemsSource = table.DefaultView;
or
dataUrl.ItemsSource = new DataView(table);
also remove command.ExecuteNonQuery();
You can use the ExecuteNonQuery to perform catalog operations (for example, querying the structure of a database or creating database objects such as tables), or to change the data in a database without using a DataSet by executing UPDATE, INSERT, or DELETE statements.
also, because you use parameters and AddWithValue(..), your query should look like this:
string sql = "SELECT rowid, url FROM urls WHERE url LIKE #search";
and you add parameter like this instead:
command.Parameters.AddWithValue("#search", "%" + txtSearch.Text + "%");
which is the whole point of using parameters
Related
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;
}
}
I want to be able to execute a custom SQL query against a table adapter that I have. Is it possible to do this? Or can I only use the predefined queries on each table adapter in the dataset design view?
If I can't do this, how would I go about executing my SQL query against a table, and having the results display in my datagridview that's bound to a table adapter?
Thanks.
EDIT: I didn't explain myself properly. I know how to Add queries to a tableadapter using the dataset designer. My issue is i need to execute a custom peice of SQL (which i build dynamically) against an existing table adapter.
I posted a comment pointing to an example using VB which creates a class that extends TableAdapter. Instead of using the VB example and rewriting it in C# I will show how this can be done without creating a class that extends TableAdapter.
Basically create a BackgroundWorker to perform the sql query. You don't have to but it would be nice. Build the query string based on input from the user.
private void queryBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//Initialize sqlconnection
SqlConnection myConnection;
//Convert date in to proper int format to match db
int fromDate = int.Parse(dateTimePickerStartDate.Value.ToString("yyyyMMdd"));
int toDate = int.Parse(dateTimePickerEndDate.Value.ToString("yyyyMMdd"));
//Setup Parameters
SqlParameter paramFromDate;
SqlParameter paramToDate;
SqlParameter paramItemNo;
SqlParameter paramCustomerNo;
//Fill the data using criteria, and throw any errors
try
{
myConnection = new SqlConnection(connectionString);
myConnection.Open();
using (myConnection)
{
using (SqlCommand myCommand = new SqlCommand())
{
//universal where clause stuff
string whereclause = "WHERE ";
//Add date portion
paramFromDate = new SqlParameter();
paramFromDate.ParameterName = "#FromDate";
paramFromDate.Value = fromDate;
paramToDate = new SqlParameter();
paramToDate.ParameterName = "#ToDate";
paramToDate.Value = toDate;
myCommand.Parameters.Add(paramFromDate);
myCommand.Parameters.Add(paramToDate);
whereclause += "(TableName.date BETWEEN #FromDate AND #ToDate)";
//Add item num portion
if (!string.IsNullOrEmpty(itemNo))
{
paramItemNo = new SqlParameter();
paramItemNo.ParameterName = "#ItemNo";
paramItemNo.Value = itemNo;
myCommand.Parameters.Add(paramItemNo);
whereclause += " AND (Tablename.item_no = #ItemNo)";
}
//Add customer number portion
if (!string.IsNullOrEmpty(customerNo))
{
paramCustomerNo = new SqlParameter();
paramCustomerNo.ParameterName = "#CustomerNo";
paramCustomerNo.Value = customerNo;
myCommand.Parameters.Add(paramCustomerNo);
whereclause = whereclause + " AND (Tablename.cus_no = #CustomerNo)";
}
string sqlquery = "SELECT * FROM TableName ";
sqlquery += whereclause;
//MessageBox.Show(sqlquery);
myCommand.CommandText = sqlquery;
myCommand.CommandType = CommandType.Text;
myCommand.Connection = myConnection;
this.exampleTableAdapter.ClearBeforeFill = true;
this.exampleTableAdapter.Adapter.SelectCommand = myCommand;
this.exampleTableAdapter.Adapter.Fill(this.ExampleDataSet.ExampleTable);
}
}
}
catch (System.Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
I personally like the idea of coding a class that extends TableAdapter but this was a quick easy way of answering the OP's question. Sorry it took a year :)
Taken from here
To add a query to a TableAdapter in the Dataset Designer
Open a dataset in the Dataset Designer. For more information, see How to: Open a Dataset in the Dataset Designer.
Right-click the desired TableAdapter, and select Add Query.
-or-
Drag a Query from the DataSet tab of the Toolbox onto a table on the designer.
The TableAdapter Query Configuration Wizard opens.
Complete the wizard; the query is added to the TableAdapter.
I read SQL Command Builder class from
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommandbuilder.aspx and I found that I can show update done on dataset/database using select and update command.
SQL Command Builder concept is clear if I am using single dataset but what if I want to use two different dataset?
Scenario: I am reading values from database into one dataset ds1; which is assign to sql adapter and sql command builder. Now, I am reading only selected values from ds1 and storing into second dataset ds2; which is not assign to sql data adapter and sql command builder.
I am concern if I am updating any data on ds2 whether it will update database or not. Also, how should I do it using SQL Command builder and SQL Adapter.
//primary dataset
ds = new ProductDataSet();
command = new SqlCommand(
"SELECT no, name, price, cost, dept FROM PRODUCTS", connection);
adapter = new SqlDataAdapter();
adapter.SelectCommand = command;
adapter.Fill(ds, "table");
Primary dataset is fill on form load event. User will enter item no of his choice which will be search from primary ds and saved/display onto 2nd ds (2nd ds is not connected with with any adapter or command builder right now). For eg; 2nd ds have 3 items.
Now say user update any information on 2nd ds it should automatically update database and display on grid.
//2nd ds2 update code
for (int i = 0; i < ds2.Tables[0].Rows.Count; i++)
{
string item = ds2.Tables[0].Rows[i][0].ToString();
command = new SqlCommand("UPDATE PRODUCTS SET " + _colName + " = '" + _newValue + "'" + "WHERE ITEM_NO = '" + item + "'", Class1.conn);
datagrid.DataSource = ds2.Tables[0];
}
According to your suggestion if I am adding/declaring adapter/builder in above code it doesn't work. I am getting Table Mapping error.
Use another SQLAdapter and SQLCommandBuilder. The example on that page shows how to update your database. You just need to supply the fields to be updated in the form of a query, such as:
SELECT Name, Address, Phone, Email FROM Contact
and the command builder will generate the proper SQL UPDATE statement.
SqlCommandBuilder automatically generates INSERT, UPDATE and DELETE sql statements based on the SELECT statement for a single table.
For the Transact-SQL statements to be generated using SqlCommandBuilder, there are 2 steps
Step 1. Set the "SelectCommand" property of the SqlDataAdapter object
SqlDataAdapter dataAdapter = new SqlDataAdapter();
dataAdapter.SelectCommand = new SqlCommand("SELECT_Query", con);
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet, "Students");
Step 2. Create an instance of SqlCommandBuilder class and associate the
SqlDataAdapter object created above using DataAdapter property of the SqlCommandBuilder object
SqlCommandBuilder builder = new SqlCommandBuilder();
builder.DataAdapter = dataAdapter;
Step 3. Updating records of ds1
dataAdapter.Update(ds1, "Students");
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.
I've written a small form that reads the data from a database table (SQL CE 3.5) and displays it in a DataGridView control. This works fine. I then modified it to make a change to the data before displaying it, which also seems to work fine with the exception that it doesn't seem to actually commit the changes to the database. The code is as follows:
using (SqlCeConnection conn = new SqlCeConnection(
Properties.Settings.Default.Form1ConnectionString
)) {
conn.Open();
using (SqlCeDataAdapter adapter = new SqlCeDataAdapter(
"SELECT * FROM People", conn
)) {
//Database update command
adapter.UpdateCommand = new SqlCeCommand(
"UPDATE People SET name = #name " +
"WHERE id = #id", conn);
adapter.UpdateCommand.Parameters.Add(
"#name", SqlDbType.NVarChar, 100, "name");
SqlCeParameter idParameter = adapter.UpdateCommand.Parameters.Add(
"#id", SqlDbType.Int);
idParameter.SourceColumn = "id";
idParameter.SourceVersion = DataRowVersion.Original;
//Create dataset
DataSet myDataSet = new DataSet("myDataSet");
DataTable people = myDataSet.Tables.Add("People");
//Edit dataset
adapter.Fill(myDataSet, "People");
people.Rows[0].SetField("name", "New Name!");
adapter.Update(people);
//Display the table contents in the form datagridview
this.dataGridView1.DataSource=people;
}
}
The form displays like so:
Looking at the table via Visual Studio's Server Explorer however, doesn't show any change to the table.
What am I doing wrong?
I found it. It took days but I found it.
Properties.Settings.Default.Form1ConnectionString is "Data Source=|DataDirectory|\Form1.sdf". The update works if I replace the automatically generated "|DataDirectory|" with the actual path. Oddly enough reading from the database works either way.
Shouldn't the update line be
adapter.Update(myDataSet, "People")
I would make sure the DataSet believes it's been changed. Invoke DataSet.HasChanges (returns bool) and DataSet.GetChanges, which returns a delta of the DataSet from the original.
Have you also tried this against Sql Server Express just to eliminate any issues with the CE data adapter?