I am new to C# and need some guidance!
short summary:
I have an application with few forms that is connected to the Access database. Everything is working fine! The user can select the needed item from the combo box that is shown in the label on the next form. Additionally the elements of the table are shown in the datagridview2 on the same form. Every item from the combo box is connected to a different table in the Access database:
private void frmData_Load(object sender, EventArgs e)
{
lblItem.Text = Item;
string connectionString = null;
OleDbConnection con;
connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\".\\Database_Example.accdb\"";
con = new OleDbConnection(connectionString);
if (lblItem.Text == "X")
{
OleDbCommand cmd2 = new OleDbCommand("SELECT ID, Column1, Column2 FROM X", con);
OleDbDataAdapter sda = new OleDbDataAdapter(cmd2);
DataTable td = new DataTable();
sda.Fill(td);
dataGridView2.DataSource = td;
}
if (lblItem.Text == "Y")
{
OleDbCommand cmd = new OleDbCommand("SELECT ID, Column1, Column2 FROM Y", con);
OleDbDataAdapter sda = new OleDbDataAdapter(cmd);
DataTable td = new DataTable();
sda.Fill(td);
dataGridView2.DataSource = td;
}
}
Now I want to use my application to add new tables to the database. Therefore there are bunch of different approaches on the web.
Problem: I´m looking for an option to automatically create new if-statements once I´ve added new tables in the database. For example for items "Z"; "A"; "B"; ...
Is there a way to do it? Or do I need a different approach?
Greetings :)
We get this question a few times a month and the answer is always the same:
You really need ONE table, with an additional column for your Z, A, B, etc. Make this the first field of the primary key.
Now this:
if (lblItem.Text == "X")
{
OleDbCommand cmd2 = new OleDbCommand("SELECT ID, Column1, Column2 FROM X", con);
OleDbDataAdapter sda = new OleDbDataAdapter(cmd2);
DataTable td = new DataTable();
sda.Fill(td);
dataGridView2.DataSource = td;
}
// etc
Becomes this:
var td = new DataTable();
using (var con = new OleDbConnection("connection string here"))
using (var cmd = new OleDbCommand("SELECT ID, Column1, Column2 FROM MyTable WHERE Key = ? ", con))
using (var sda = new OleDbDataAdapter(cmd2))
{
cmd.Parameters.Add("?", OleDbType.NChar, 3).Value = lblItem.Text;
sda.Fill(td);
}
No if statement required.
Additionally, do NOT try to re-use your connection object throughout a form instance like that. It really is more efficient to create a new connection for each call to the database.
Related
void LoadData()
{
DataTable dtshow = new DataTable();
DataTable dt = new DataTable();
if (con.State == ConnectionState.Closed)
{
con.Open();
}
//query get data show in grid
SqlCommand cmdshow = new SqlCommand("SELECT a,b,c FROM XXX", con);
//query check database change
SqlCommand cmd = new SqlCommand("SELECT a,b,c FROM XXX", con);
cmd.Notification = null;
SqlDependency de = new SqlDependency(cmd);
de.OnChange += new OnChangeEventHandler(de_OnChange);
dt.Load(cmd.ExecuteReader(CommandBehavior.CloseConnection));
if (con.State == ConnectionState.Closed)
{
con.Open();
}
dtshow.Load(cmdshow.ExecuteReader(CommandBehavior.CloseConnection));
dataGridView1.DataSource = dtshow;
}
Hi, all my code is above,
When I update "a" column from the specific row, I need to find "b" and "c" from that row.
How can I get that ??
Thanks
Im not sure what SQL you are using but lets take PSQL:
its possible to add a RETURNING value when updating
for Example:
UPDATE xxx SET a = <what ever>
WHERE <your condition>
RETURNING b,c;
Salam Alekom .
I have comboBox filling it from database as
SqlConnection con = new SqlConnection(strcon);
con.Open();
SqlCommand scm = new SqlCommand();
scm.Connection = con;
scm.CommandText = "select * from com";
SqlDataAdapter adpt = new SqlDataAdapter(scm);
DataTable dt = new DataTable();
adpt.Fill(dt);
comboBox1.DataSource = dt;
comboBox1.DisplayMember = dt.Columns["com_name"].ToString();
comboBox1.ValueMember = dt.Columns["com_id"].ToString();
Where filling with all data that store in the table from database.
This comboBox use also to insert to another table in database
This also poses no problem
The problem when making search in table to get some data. I need to get the text value that store in table and equal to text value of comboBox and sort it in position 0 of this comboBox
I made the search no problem but what property or method that allow me to put the value in position 0 with out affecting the other values that be in combobox
SqlConnection con = new SqlConnection(strcon);
con.Open();
using(SqlCommand scm2 = new SqlCommand())
{
scm2 .Connection = con;
scm2.CommandType = CommandType.StoredProcedure;
scm2 .CommandText = "SP_retrieve_data";
scm2.Parameters.AddWithValue ("#id", textBox1.Text);
SqlDataReader dr = scm2.ExecuteReader();
while (dr.Read())
{
comboBox1. = dr["com_name"].ToString();//what code accept the value
}
}
How can I save a DataTable to a file. accdb (Access) existing one? I've used the following code and it does not work:
using (OleDbConnection oledbConnection = new OleDbConnection(connection))
{
oledbConnection.Open();
string query = "SELECT * FROM Student";
using (OleDbCommand oledbCommand = new OleDbCommand(query, oledbConnection))
{
using (OleDbDataAdapter oledbDataAdapter = new OleDbDataAdapter(oledbCommand))
{
using (OleDbCommandBuilder oledbCommandBuilder = new OleDbCommandBuilder(oledbDataAdapter))
{
oledbDataAdapter.DeleteCommand = oledbCommandBuilder.GetDeleteCommand(true);
oledbDataAdapter.InsertCommand = oledbCommandBuilder.GetInsertCommand(true);
oledbDataAdapter.UpdateCommand = oledbCommandBuilder.GetUpdateCommand(true);
oledbDataAdapter.Update(dataTable);
}
}
}
oledbConnection.Close();
}
The variable dataTable is initialized with the original contents of the file, then it was modified by adding a row and now I have to update the table in the database.
I tried using the following code, but that does not work :(
OleDbDataAdapter da = new OleDbDataAdapter("SELECT * FROM Student", connection);
OleDbCommandBuilder cmdBuilder = new OleDbCommandBuilder(da);
da.InsertCommand = cmdBuilder.GetInsertCommand(true);
// create and insert row in the DataTable
da.Update(dataTable);
Assuming that you have made some changes in the datatable, then you could pass the generated update/insert/delete command to the adapter in this way
oledbDataAdapter.DeleteCommand = oledbCommandBuilder.GetDeleteCommand();
oledbDataAdapter.InsertCommand = oledbCommandBuilder.GetInsertCommand();
oledbDataAdapter.UpdateCommand = oledbCommandBuilder.GetUpdateCommand();
oledbDataAdapter.Update(datatable);
Now the adapter knows how to update your table
I have the following:
String sql = "SELECT * FROM Temp WHERE Temp.collection = '" + Program.collection + "'";
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(sql, conn);
Program.defaultCollection = (String)cmd.ExecuteScalar();
And I want to get the second column after executing the statement. I know it will return only one row with two columns
I have read online that I will have to read each row of the result, is there any other way?
ExecuteScalar gets the first column from the first row of the result set. If you need access to more than that you'll need to take a different approach. Like this:
DataTable dt = new DataTable();
SqlDataAdapater sda = new SqlDataAdapter(sql, conn);
sda.Fill(dt);
Program.defaultCollection = dt.Rows[0]["defaultCollection"];
Now, I realize that the field name may not be defaultCollection, but you can fill that in.
From the MSDN documentation for ExecuteScalar:
Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
Now, as a final bit of advice, please wrap all ADO.NET objects in a using statement. Like this:
using (SqlConnection conn = new SqlConnection(connString))
using (SqlDataAdapter sda = new SqlDataAdapter(sql, conn))
{
DataTable dt = new DataTable();
sda.Fill(dt);
// do something with `dt`
}
this will ensure they are properly disposed.
And I want to get the second column after executing the statement
It is not possible with execute scalar.
is there any other way
You have 2 options here either to use SqlDataAdapter or SqlDataReader.
For you using DataReader is a recommended approach as you don't need offline data or do other worh
by using SqlDataAdapter
using (SqlConnection c = new SqlConnection(
youconnectionstring))
{
c.Open();
/
using (SqlDataAdapter a = new SqlDataAdapter(sql, c))
{
DataTable t = new DataTable();
a.Fill(t);
if(t.Rows.Count > 0)
{
string text = t.Rows[0]["yourColumn"].ToString();
}
}
}
by using DataREader
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(sql, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
//read data here
string text = reader.GetString(1)
}
reader.Close();
}
SqlCommand.ExecuteScalar() can be used only when the result have just one row and one column.
If you need more than one column to be returned, you should use something like this:
String sql = "SELECT * FROM Temp WHERE Temp.collection = '" + Program.collection + "'";
SqlConnection conn = new SqlConnection(connString);
using(SqlCommand cmd = new SqlCommand(sql, conn))
{
using(SqlDataReader rdr = cmd.ExecuteReader())
{
if(rdr.Read())
{
Program.defaultCollection = (String)rdr["Column1"];
Program.someOtherVar = (String)rdr["Column2"];
}
}
rdr.Close();
}
That will be the fastest way.
You can use a DataReader and read only the first column like:
IDataReader cReader = cmd.ExecuteReader();
if(cReader.Read())
{
string cText = cReader.GetString(1); // Second Column
}
ExecuteScalar only returns one value. You have to make sure your query only returns that value.
String sql = "SELECT temp.defaultCollection FROM Temp WHERE Temp.collection = '" + Program.collection + "'";
On a side note, read on SqlParameter. You don't want to concatenate values like that, you'll have a problem when the collection property contains a quote.
I' m trying to insert a new DataRow (locally stored in a DataSet) to a Access table with C#. The table is also created by this app and has 3 columns:
ID (integer, primary key, Required is set to No though I've set it as primary key)
Name (string)
Money (int)
My current code looks like that:
OleDbConnection con = new OleDbConnection();
OleDbCommand cmd = new OleDbCommand();
OleDbDataAdapter da;
DataSet ds = new DataSet();
DataView dv = new DataView();
DataRow row;
string Con = #"Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=";
string path = "V:\\ProjectProgress\\Test.mdb";
con.ConnectionString = Con + path;
if (con.State == ConnectionState.Closed)
{
con.Open();
}
cmd.Connection = con;
cmd.CommandText = "SELECT ID, Name, Money FROM Test";
da = new OleDbDataAdapter(cmd);
da.TableMappings.Add("Table", "Test");
da.Fill(ds, "Test");
ds.Tables["Test"].Columns[0].AutoIncrement = true;
ds.Tables["Test"].Columns[0].AutoIncrementSeed = -1;
ds.Tables["Test"].Columns[0].AutoIncrementStep = -1;
dv.Table = ds.Tables["Test"];
row = ds.Tables["Test"].NewRow();
row["Name"] = "Huber";
row["Money"] = 100;
ds.Tables["Test"].Rows.Add(row);
string strOLE = "INSERT INTO Test ([Name], [Money]) Values(#Name, #Money)";
OleDbCommand cmdi = new OleDbCommand(strOLE, con);
cmdi.Parameters.Add("#Name", OleDbType.VarChar, 25, "Name");
cmdi.Parameters.Add("#Money", OleDbType.Integer, 4, "Money");
da.InsertCommand = cmdi;
da.Update(ds.Tables["Test"]);
con.Close();
When updating I'm always getting a
Index or primary key cannot contain a Null value
error.
Setting the Required value of the ID column to Yes, will throw a
Index or Primary Key Cannot Contain a Null Value
error.
How can I let Access assign the right primary key and how do I get the new value back into my dataset?
Using SCOPE_IDENTITY() is not possible in Access as far as I know and found in some forums.
(Working with Visual C# Express 2010, Access 2003)
The following is complete working test code to illustrate the procedure. All we need to do is provide an OleDbDataAdapter.SelectCommand that includes the primary key and the columns we want to update, and the OleDbCommandBuilder object will create the INSERT statement for us:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.OleDb;
namespace oleDbTest
{
class Program
{
static void Main(string[] args)
{
string myConnectionString;
myConnectionString =
#"Provider=Microsoft.ACE.OLEDB.12.0;" +
#"Data Source=C:\Users\Public\Database1.accdb;";
using (OleDbConnection con = new OleDbConnection())
{
con.ConnectionString = myConnectionString;
con.Open();
using (OleDbDataAdapter da = new OleDbDataAdapter())
using (OleDbCommandBuilder bld = new OleDbCommandBuilder(da))
{
bld.QuotePrefix = "["; // these are
bld.QuoteSuffix = "]"; // important!
da.SelectCommand = new OleDbCommand(
"SELECT [ID], [Name], [Money] " +
"FROM [Test] " +
"WHERE False",
con);
using (System.Data.DataTable dt = new System.Data.DataTable("Test"))
{
// create an empty DataTable with the correct structure
da.Fill(dt);
System.Data.DataRow dr = dt.NewRow();
dr["Name"] = "Huber";
dr["Money"] = 100;
dt.Rows.Add(dr);
da.Update(dt); // write new row back to database
}
}
con.Close();
}
Console.WriteLine();
Console.WriteLine("Done.");
}
}
}
My first thought is that your problem might be that both AutoIncrementSeed and AutoIncrementStep are set to a negative value of -1. Try setting both to a positive value.
[EDIT]
Second thought, you might want to try the OleDbCommandBuilder (MSDN documentation here). It creates your INSERT, UPDATE, and DELETE statements automatically using the column information of your DataSet.
At my former employer we used OleDbCommandBuilder all the time when working with Access and it worked like a charm, even with auto increment fields.