try
{
OleDbConnection myConnection = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Users\\HP8200\\Desktop\\ELISA2014Data.mdb ;Persist Security Info=False;");
myConnection.Open();
// Create Oledb command to execute particular query
OleDbCommand myCommand = new OleDbCommand();
myCommand.Connection = myConnection;
// Query to create table with specified data columne
myCommand.CommandText = "CREATE TABLE UXZona([IDZona] int, [Morada] text)";
//myCommand.ExecuteNonQuery();
MessageBox.Show("Tabela criada");
}
catch
{
OleDbConnection myConnection = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Users\\HP8200\\Desktop\\ELISA2014Data.mdb ;Persist Security Info=False;");
myConnection.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "INSERT INTO UXZona (IDZona, Morada) VALUES ('" +
transaction.UnloadPlaceAddress.AddressID + "','" +
transaction.UnloadPlaceAddress.AddressLine2 + "')";
cmd.ExecuteNonQuery();
MessageBox.Show("Dados inseridos");
}
I need to insert data into the database but it isn't working. I launch the program and there are no errors, I do everything but when I check the database the table is empty.
UPDATE
Now when i launch the program I have this error:
"System.InvalidOperationException: 'ExecuteNonQuery: Connection property has not been initialized." on cmd.ExecuteNonQuery();
There are a number of things wrong! I give below corrected code:
try
{
bool success = false;
using (var myConnection = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Users\\HP8200\\Desktop\\ELISA2014Data.mdb ;Persist Security Info=False;"))
{
// Create Oledb command to execute particular query
using (var myCommand = new OleDbCommand())
{
myCommand.Connection = myConnection;
// Query to create table with specified data columne
//myCommand.CommandText = "CREATE TABLE UXZona([IDZona] int, [Morada] text)";
//myCommand.ExecuteNonQuery();
//MessageBox.Show("Tabela criada");
cmd.CommandType = CommandType.Text;
cmd.CommandText = "INSERT INTO UXZona (IDZona, Morada) VALUES (#id, #morada)";
var param = cmd.CreateParameter();
param.ParameterName = "#id";
param.OleDbType = OleDbType.Integer;
param.Value = transaction.UnloadPlaceAddress.AddressID;
cmd.Parameters.Add(param);
param = cmd.CreateParameter();
param.ParameterName = "#morada";
param.OleDbType = OleDbType.VarChar;
param.Value = transaction.UnloadPlaceAddress.AddressLine2;
cmd.Parameters.Add(param);
myConnection.Open();
if (cmd.ExecuteNonQuery() == 1)
{
success = true;
}
}
}
if (success)
{
MessageBox.Show("Dados inseridos");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
By way of explanation. I have commented out (but not deleted) all references to creating the table. Table creation and table insertion should be in two different routines. Normally you only create a table once, but insert is probably called many times.
I have placed the OleDbConnection and OleDbCommand within using loops. This is good practice, as they both implement IDisposable. Writing your code like this means that the Garbage Collector (GC) knows immediately that it can safely dispose of the objects after use.
I have changed the insert statement such that it takes parameters. This is highly recommended practice to safeguard against SQL Injection (if you do not know what this is please Google it). In fact Access is relatively immune from the worst forms of SQL Injection, because it rejects any command that contains multiple statements, but please get into good habits. With time you will progress to other databases which do not have this restriction.
I deliberately wait before opening the connection until just before it is needed. Connections consume resources, so it is good practice to use them as sparingly as possible. Also for this reason, I have moved your success message outside of the using loops. This means that the cleanup of resources is not waiting for the user to click OK in the message box.
Finally try catch is all well and good, but normally you want to know why the error occurred. Hence you add (Exception ex) to catch so that you can find the reason.
PS What I forgot to mention. In your original INSERT, you were surrounding both VALUES with single quotes. Only use single quotes for strings/text. Integers and other numbers require no quotes. If you quote them, the database will treat it as a string and you will get a data type error.
Related
This question already has answers here:
Inserting Multiple Records into SQL Server database using for loop
(5 answers)
Closed 6 months ago.
can we execute multiple insert statements into MS Access DB 2016 using OLEDB without closing connection/Keeping session alive? I tried moving the OleDB command instantiation, con.open() and con.close() outside of foreach loop but it does not work. Any suggestions?
foreach (var list in lsobj)
{
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = con;
cmd.CommandType = CommandType.Text;
con.Open();
cmd.CommandText = "INSERT INTO tbl(file_dt, name, created_dt)";
"VALUES (#file_dt,#name,#inserted_dt)";
cmd.Parameters.AddWithValue("#file_dt", list.file_dt.ToString());
cmd.Parameters.AddWithValue("#name", list.name);
cmd.Parameters.AddWithValue("#inserted_dt", list.inserted_dt.ToString());
cmd.ExecuteNonQuery();
con.Close();
}
Yes, you can. As a general rule, before .net days, it was quite much the "norm" to open a connection, and keep it open. However, .net system tends to cache and re-use the connections you made, even when closed.
but, for the most part, yes, you can execute multiple statements, and do them with the same connection.
So, say this code example:
void TestFun()
{
using (OleDbConnection conn = new OleDbConnection(Properties.Settings.Default.AccessDB))
{
using (OleDbCommand cmdSQL = new OleDbCommand("", conn))
{
conn.Open();
// save picture as bytes to DB
byte[] fData = File.ReadAllBytes(txtFile.Text);
string strSQL =
#"INSERT INTO MyPictures (FileName, PictureData)
VALUES(#File, #Data)";
cmdSQL.CommandText = strSQL;
cmdSQL.Parameters.Add("#File", OleDbType.VarWChar).Value = txtFileName.Text;
cmdSQL.Parameters.Add("#Data", OleDbType.Binary).Value = fData;
cmdSQL.ExecuteNonQuery();
// display data in our grid
cmdSQL.CommandText = "SELECT ID, FileName FROM MyPIctures";
cmdSQL.Parameters.Clear();
DataTable rstData = new DataTable();
rstData.Load(cmdSQL.ExecuteReader());
dataGridView1.DataSource = rstData;
// do more commands - still same connection
}
}
}
Now, you can't execute multiple SQL statements in one "go" like you can with SQL server (just separate several statements by a ";".
However, you can certain create one connection (and even one command object), and re-use it over and over.
And since the connection and cmdSQL object are inside of a using block, then both objects, and including your connection will be closed and tidy up after you are done.
FYI: USE Parameters . Add, NOT add with value.
Now, in your case? Since it is the same command over and over - but ONLY the parameters change?
Then this (air code warning)
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = con;
cmd.CommandType = CommandType.Text;
con.Open();
cmd.CommandText = #"INSERT INTO tbl (file_dt, name, created_dt)
VALUES (#file_dt,#name,#inserted_dt)";
cmd.Parameters.Add("#file_dt", OleDbType.VarWChar);
cmd.Parameters.Add("#name", OleDbType.VarWChar);
cmd.Parameters.Add("#inserted_dt", OleDbType.VarWChar);
foreach (var list in lsobj)
{
cmd.Parameters["#file_dt"].Value = list.file_dt.ToString();
cmd.Parameters["#name"].Value = list.name;
cmd.Parameters["#inserted_dt"].Value = list.inserted_dt.ToString());
cmd.ExecuteNonQuery();
}
con.Close();
Now, I always wrap above in a using block. But, as above again shows, we are free to create a command object, and use it for "many" commands as per first example. and in the 2nd example, we ONLY have to setup the parameters one time, and then use the same connection, same command object over and over.
I have a routine where I update the local database with other database data.
I only execute a DELETE and then an INSERT INTO tblX (SELECT * FROM tblY (tblY is a linked table)), as below.
The problem is that, in some cases the SELECT takes a long time after the DELETE and I´d like to diminish the possibility of the user to make a request to this table while it´s processing.
I´d like to know if there is some mechanism to execute the DELETE only after the return of the SELECT.
conn = new OleDbConnection(Conexao.getConexaoPainelGerencialLocal());
conn.Open();
OleDbCommand cmd = new OleDbCommand(" DELETE * FROM tblClienteContato; ", conn);
cmd.ExecuteNonQuery();
cmd = new OleDbCommand(" INSERT INTO tblClienteContato " +
" SELECT * FROM tblClienteContatoVinculada;", conn);
cmd.ExecuteNonQuery();
It sounds like what you need to do is wrap both of those commands in a transaction.
The cool thing about a transaction is that it either ALL WORKS or ALL FAILS, meaning that if something happens to stop the select statement, the database will not finalise the delete statement.
This looks like a really good example to work with:
https://msdn.microsoft.com/en-us/library/93ehy0z8(v=vs.110).aspx
Note that they have one command object, and replace the CommandText, rather than create a new object each time. This is probably important.
Try something like this:
conn = new OleDbConnection(Conexao.getConexaoPainelGerencialLocal());
OleDbCommand cmd = new OleDbCommand();
OleDbTransaction transaction = null;
try {
conn.Open();
transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted);
cmd.Connection = conn;
cmd.Transaction = transaction;
cmd.CommandText = " DELETE * FROM tblClienteContato; ";
cmd.ExecuteNonQuery();
cmd.CommandText = " INSERT INTO tblClienteContato " +
" SELECT * FROM tblClienteContatoVinculada;";
cmd.ExecuteNonQuery();
// The data isn't _finally_ completed until this happens
transaction.Commit();
}
catch (Exception ex)
{
// Something has gone wrong.
// do whatever error messaging you do
Console.WriteLine(ex.Message);
try
{
// Attempt to roll back the transaction.
// this means your records won't be deleted
transaction.Rollback();
}
catch
{
// Do nothing here; transaction is not active.
}
}
You should look into BeginTransaction, Commit and rollback, here's an example:
_con.Open();
_con_trans = _con.BeginTransaction();
using(SqlCommand cmd = _con.CreateCommand())
{
cmd.CommandText = "delete from XXXXX";
cmd.CommandType = CommandType.Text;
cmd.Transaction = _con_trans;
cmd.ExecuteNonquery();
}
using(SqlCommand cmd = _con.CreateCommand())
{
cmd.CommandText = "insert into XXXX";
cmd.CommandType = CommandType.Text;
cmd.Transaction = _con_trans;
cmd.ExecuteNonquery();
}
_con_trans.Commit();
_con_trans = null;
_con.Close();
This way, everything is wrapped under a single transaction, so when the delete begins, the table will be locked for reading and writing.
Without knowing the schema of the table, it is hard to identify why the delete process is taking an extended amount of time.
An alternative to wrapping the commands within a transaction would be to simply delete the table itself rather than the data within it by using the DROP TABLE command. And then you can recreate the table utilizing the SELECT...INTO...FROM statement to recreate. A potential advantage to this is that the schemas will match identically, and any inherent conversions (eg decimal to int) will not need to be done.
using (conn = new OleDbConnection(Conexao.getConexaoPainelGerencialLocal())) {
conn.Open();
using (OleDbCommand cmd = new OleDbCommand()) {
cmd.CommandText = "DROP TABLE tblClienteContato; ";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT * INTO tblClienteContato FROM tblClienteContatoVinculada;";
cmd.ExecuteNonQuery();
}
}
The following does not apply here (MS Access), but may to other SQL variants
Another option is to utilize the TRUNCATE command, which will delete everything in the table in one fell swoop. There is no logging of the individual rows and the indexes (if present) don't need to be recalculated on each and every line being deleted. The catch to this method is that this will not work within the transaction. If there is an Identity column the value will be reset as well. There are other potential cons to this but without knowing the design of the table I have no way of identifying them.
using (conn = new OleDbConnection(Conexao.getConexaoPainelGerencialLocal())) {
conn.Open();
using (OleDbCommand cmd = new OleDbCommand()) {
cmd.CommandText = "TRUNCATE TABLE tblClienteContato; ";
cmd.ExecuteNonQuery();
cmd.CommandText = " INSERT INTO tblClienteContato " +
" SELECT * FROM tblClienteContatoVinculada;";
cmd.ExecuteNonQuery();
}
}
As Greg commented, I created temporary tables to receive data from the external database and then I tranfer the data to the definitive tables, so that the probability of the users being impacted is very low.
I was wondering if it is possible for the update button to save the changes made in the table. I wrote this code but I have no idea how it could possibly work
This is the code i wrote for the update button:
string conString = "Data Source=MIRANDA-PC;Initial Catalog=Futebol do Rosa;Integrated Security=True";
SqlConnection con = new SqlConnection(conString);
string selectSql = "Update Players$ set Player Name='" + dataGridView2.Text + "";
SqlCommand cmd = new SqlCommand(selectSql, con);
con.Open();
This is the table I want to update the values in:
Well, you just need to execute your query with ExecuteNonQuery.
But more important, you should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Also use using statement to dispose your SqlConnection and SqlCommand.
And if your table or column names more than one word, you need to use them with [] as [Player Name]. And honestly, it is a little bit weird to use $ sign in a table name.
using(SqlConnection con = new SqlConnection(conString))
using(SqlCommand cmd = con.CreateCommand())
{
cmd.CommandText = "Update Players$ set [Player Name] = #name";
cmd.Parameters.Add("#name", SqlDbType.NVarChar, 16).Value = dataGridView2.Text;
con.Open();
cmd.ExecuteNonQuery();
}
You have to execute your SQL query with your db object.
dbinstance.ExecuteSqlCommand(string sqlcommand, object[] params);
This method is both for DDL and DML.
you can also use ExecuteNonQuery method.
cmd.CommandText = "Update Players$ set [Player Name] = #Playername";
cmd.Parameters.Add("#Playername", SqlDbType.NVarChar, 16).Value = dataGridView2.Text;
con.Open();
cmd.ExecuteNonQuery();
The best solution (if possible) to to convert your DAL (Data Access Layer) to Entity-framework based, instead of writing your own SQL queries. This is safe-by-design and never is vulnerable to SQL Injection of any kind.
Here is some mockup code:
using (AppEntities currDb = new AppEntities)
{
Players PlayerToEdit =
from player in currDb.Players
where player.PlayerID == lngPlayerID
select player.First();
PlayerToEdit.PlayerName = dataGridView2.Text;
currDb.SaveChanges();
}
You can read about it some more here:
https://msdn.microsoft.com/en-us/data/ef.aspx
I am going to implement the database access layer of the Window application using C#. The database (.accdb) is located to the project files. When it comes to two notebooks (clients) connecting to one access database through switches, it throws DBConcurrency Exception Error. My target is to check the timestamp of the sql executed first and then run the sql .
Would you please provide me some guidelines to achieve this ?
The below is my code
protected void btnTransaction_Click(object sender, EventArgs e)
{
string custID = txtID.Text;
string CompName = txtCompany.Text;
string contact = txtContact.Text;
string city = txtCity.Text;
string connString = ConfigurationManager.ConnectionStrings["CustomersDatabase"].ConnectionString;
OleDbConnection connection = new OleDbConnection(connString);
connection.Open();
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
OleDbTransaction transaction = connection.BeginTransaction();
command.Transaction = transaction;
try
{
command.CommandText = "INSERT INTO Customers(CustomerID, CompanyName, ContactName, City, Country) VALUES(#CustomerID, #CompanyName, #ContactName, #City, #Country)";
command.CommandType = CommandType.Text;
command.Parameters.AddWithValue("#CustomerID", custID);
command.Parameters.AddWithValue("#CompanyName", CompName);
command.Parameters.AddWithValue("#ContactName", contact);
command.Parameters.AddWithValue("#City", city);
command.ExecuteNonQuery();
command.CommandText = "UPDATE Customers SET ContactName = #ContactName2 WHERE CustomerID = #CustomerID2";
command.CommandType = CommandType.Text;
command.Parameters.AddWithValue("#CustomerID2", custIDUpdate);
command.Parameters.AddWithValue("#ContactName2", contactUpdate);
command.ExecuteNonQuery();
adapter.Fill(table);
GridView1.DataSource = table;
GridView1.DataBind();
transaction.Commit();
lblMessage.Text = "Transaction successfully completed";
}
catch (Exception ex)
{
transaction.Rollback();
lblMessage.Text = "Transaction is not completed";
}
finally
{
connection.Close();
}
}
I notice a couple of things:
Your INSERT CommandText has a #Country parameter specified, but you don't add a corresponding parameter to the .Parameters collection.
You try to re-use the command object for your UPDATE by changing the CommandText and adding more parameters, but you don't get rid of the previous parameters from your INSERT so presumably they're still in place, which could definitely confuse the UPDATE command.
You specify the UPDATE parameters in the wrong order. ACE.OLEDB ignores parameter names, so the parameters must be added to the .Parameters collection in the exact order that they appear in the CommandText.
I'm also confused as to:
why you don't commit the transaction before trying to bind the GridView, and
why you need a transaction at all, since the INSERT and the UPDATE appear to be unrelated.
I am having a table which has three fields, namely LM_code,M_Name,Desc. LC_code is a autogenerated string Id, keeping this i am updating M_Name and Desc. I used normal update command, the value is passing in runtime but the fields are not getting updated. I hope using oledb parameters the fields can be updated.
Here is my code.
public void Modify()
{
String query = "Update Master_Accounts set (M_Name='" + M_Name + "',Desc='" + Desc + "') where LM_code='" + LM_code + "'";
DataManager.RunExecuteNonQuery(ConnectionString.Constr, query);
}
In DataManager Class i am executing the query string.
public static void RunExecuteNonQuery(string Constr, string query)
{
OleDbConnection myConnection = new OleDbConnection(Constr);
try
{
myConnection.Open();
OleDbCommand myCommand = new OleDbCommand(query, myConnection);
myCommand.ExecuteNonQuery();
}
catch (Exception ex)
{
string Message = ex.Message;
throw ex;
}
finally
{
if (myConnection.State == ConnectionState.Open)
myConnection.Close();
}
}
private void toolstModify_Click_1(object sender, EventArgs e)
{
txtamcode.Enabled = true;
jewellery.LM_code = txtamcode.Text;
jewellery.M_Name = txtaccname.Text;
jewellery.Desc = txtdesc.Text;
jewellery.Modify();
MessageBox.Show("Data Updated Succesfully");
}
This annoyed me, screwy little OleDB, so I'll post my solution here for posterity. It's an old post but seems like a good place.
OleDB doesn't recognize named parameters, but it apparently does recognize that you're trying to convey a named parameter, so you can use that to your advantage and make your SQL semantic and easier to understand. So long as they're passed in the same order, it'll accept a variable as a named parameter.
I used this to update a simple Access database in a network folder.
using (OleDbConnection conn = new OleDbConnection(connString))
{
conn.Open();
OleDbCommand cmd = conn.CreateCommand();
for (int i = 0; i < Customers.Count; i++)
{
cmd.Parameters.Add(new OleDbParameter("#var1", Customer[i].Name))
cmd.Parameters.Add(new OleDbParameter("#var2", Customer[i].PhoneNum))
cmd.Parameters.Add(new OleDbParameter("#var3", Customer[i].ID))
cmd.Parameters.Add(new OleDbParameter("#var4", Customer[i].Name))
cmd.Parameters.Add(new OleDbParameter("#var5", Customer[i].PhoneNum))
cmd.CommandText = "UPDATE Customers SET Name=#var1, Phone=#var2" +
"WHERE ID=#var3 AND (Name<>#var4 OR Phone<>#var5)";
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
}
}
It may look like an excess of code, and yes you're technically repeating yourself, but this makes it worlds easier when you're playing connect-the-dots later on.....
You are close with the rest of your connection and such, but as you note, doing it with parameterized queries is safer from SQL-Injection...
// Some engines used named parameters, others may not... The "?"
// are "place-holders" for the ordinal position of parameters being added...
String MyQuery = "Update MyTable set SomeField = ?, AnotherField = ? "
+ " where YourKeyField = ?";
OleDbCommand MyUpdate = new OleDbCommand( MyQuery, YourConnection );
// Now, add the parameters in the same order as the "place-holders" are in above command
OleDbParameter NewParm = new OleDbParameter( "ParmForSomeField", NewValueForSomeField );
NewParm.DbType = DbType.Int32;
// (or other data type, such as DbType.String, DbType.DateTime, etc)
MyUpdate.Parameters.Add( NewParm );
// Now, on to the next set of parameters...
NewParm = new OleDbParameter( "ParmForAnotherField", NewValueForAnotherField );
NewParm.DbType = DbType.String;
MyUpdate.Parameters.Add( NewParm );
// finally the last one...
NewParm = new OleDbParameter( "ParmForYourKeyField", CurrentKeyValue );
NewParm.DbType = DbType.Int32;
MyUpdate.Parameters.Add( NewParm );
// Now, you can do you
MyUpdate.ExecuteNonQuery();
Just to add to RJB's answer, it's a little-known fact that OleDb actually DOES accept named parameters. You've just got to declare the parameters in SQL as well.
See: low-bandwidth.blogspot.com.au/2013/12/positional-msaccess-oledb-parameters.html
If you DON'T declare the parameters in SQL, OleDb uses purely positional parameter insertion, and it doesn't matter if the names of the parameters match the SQL, or if parameters are used twice in the SQL - it will just go through and blindly replace any found parameters in the SQL in order from start to end, with those passed.
However if you DO declare the parameters correctly, you get the benefit of named parameters and parameters allowed to be repeated multiple times within the SQL statement.