I have a c# .net program where I need to first insert data into a table using a sql connection and then adjust the same set of data using ADO.net. I am not sure how to make sure the insert via the sql connection is complete before doing the ado.net changes. I am getting a concurrency violation when I try the code below. I would guess that this is a race condition problem.
I am getting a concurrency violation error at the point of the UpdateAll statement and I can't seem to work around it
Thanks for the help.
Below is an example of the code with the SQL and ado.net changes dramatically simplified.
try
{
String deleteQuery = "DELETE FROM dbo.TABLENAME";
String reportQuery = #"
INSERT INTO TABLENAME
(
COLUMN1,
COLUMN2,
COLUMN3
)
SELECT
COLUMN1,
COLUMN2,
COLUMN3
FROM OTHERTABLES
";
SqlConnection ReportConnect = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.Text;
cmd.Connection = ReportConnect;
cmd.CommandTimeout = Convert.ToInt32(Properties.Settings.Default.ReportTimeout.ToString());
ReportConnect.Open();
cmd.CommandText = deleteQuery;
cmd.ExecuteNonQuery();
cmd.CommandText = reportQuery;
cmd.ExecuteNonQuery();
ReportConnect.Close();
ReportConnect.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
try
{
foreach (DataRow dr in DataSet.TABLENAME)
{
dr[0] = whatever;
dr[0] = 100;
dr[0] = 42.42;
}
}
catch (Exception ax)
{
MessageBox.Show(ax.Message);
}
finally
{
this.tableAdapterManager.UpdateAll(this.DataSet);
}
The problem here is that the "tableAdapterManager" appears to be created and opened before the data changes are made (with the sqlcommand). If you create the SqlDataAdapter with the wizard, by default the concurrency mode is optimistic (so the update and delete statement detect if the database has changed...) and fails with the exception you expose.
You can solve this issue in the wizard windows "Generate the SQL statements", click on the "Advanced Options" and uncheck the "Use optimistic concurrency" option.
Also you can change this from the form.designes.cs file, look for the UpdateCommand of the SqlDataAdapter and make sure that in the creation of the SqlParameter the DataRowVersion is set to "Default" or use another constructor.
In theory ExecuteNonQuery would not complete until the SQL had run, rendering your question moot. If you were deliberately executing asynchronously it would be a different matter, but you're not.
You should still be aware of issues caused by multiple concurrent users, of course.
Related
I'm using MySQL to try and add a new user to my database. User got an Id, a First Name, a Last Name and a Date of Birth. But when I run the code below (And run conn.close() after I'm done) the database tells me (using HeidiSQL) that in the Table Overview there is now a new row in the table but when I open the Data Tab to look at the rows, there is nothing. It's empty. Running a COUNT(*) also returns 0.
using (MySqlTransaction transaction = conn.BeginTransaction())
{
using (MySqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = "INSERT INTO USERS(NAME_FIRST,NAME_LAST,DATE_OF_BIRTH) VALUES(#nameFirst,#nameLast,#dateOfBirth)";
cmd.Transaction = transaction;
cmd.Parameters.AddWithValue("#nameFirst", user.NameFirst);
cmd.Parameters.AddWithValue("#nameLast", user.NameLast);
cmd.Parameters.AddWithValue("#dateOfBirth", user.DateOfBirth);
cmd.Prepare();
cmd.ExecuteNonQuery();
lastInsertId = (uint)cmd.LastInsertedId;
}
}
I get no errors. Nothing shows up in any log and everyone sees the same as me.
What am I doing wrong?
It feels like it's the use of begintransaction which starts a transaction. This means autocommit=false for the entirety of the transaction.
After ExecuteNonQuery Do a transaction.Commit(); and see if they show up.
More Info Here
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.
In my code I am trying to CREATE INDEX in my code, and since CREATING duplicated INDEX is not allowed, I wanted to check if my INDEX existed in my SQL CE Database.
In C# code, I ran the query using IDbCommand with query "SELECT * FROM INFORMATION_SCHEMA.INDEXES WHERE TABLE_NAME = 'mytablename'" so if there are results, I have the INDEX already created for this Database. Nothing fancy. However, when I run this, I did not see the result even though I know I created the index.
So I tried just running the query using SQL Server Compact/SQLite Toolbox. I am seeing that I have the INDEX with the tool's querying. I thought my SQL syntax might be wrong so I ran query "SELECT * FROM INFORMATION_SCHEMA.INDEXES" without WHERE to compare how many results I receive. I am seeing 13 results vs 12 results. Obviously the missing one is mytablename.
Does anybody have clue why this weird issue is occuring? If this is does not resolve, I can try catch and catch if there is duplicate and ignore the result. But preferably, I would like to properly catch if the Row exists or not.
The C# code I used is following.
DbProviderFactory factory = DbProviderFactories.GetFactory(provider);
using (DbConnection conn = factory.CreateConnection())
{
conn.ConnectionString = "xxxx";
try
{
conn.Open();
IDbCommand cmd = factory.CreateCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT * FROM INFORMATION_SCHEMA.INDEXES WHERE TABLE_NAME = 'mytablename'";
DbDataReader ddr = (DbDataReader)cmd.ExecuteReader();
ddr.Read();
}
catch (Exception ex)
{
}
finally
{
conn.Close();
}
}
This code is supposed to save some values in textboxes to a specific row. The code runs just fine with no hiccups, but refuses to actually update the database no matter what I do.
try
{
using (var con = new OleDbConnection())
{
con.ConnectionString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\User\Desktop\esoft\gym\gym\bin\Debug\Clients.accdb;";
con.Open();
using (var com = new OleDbCommand())
{
com.Connection = con;
com.CommandText = "UPDATE gym SET BMI = #bmi and Health = #health and weight_change_to_healthy_bmi = #weight WHERE ID = #id";
com.Parameters.AddWithValue("#bmi", bmi.Text);
com.Parameters.AddWithValue("#health", health.Text);
com.Parameters.AddWithValue("#weight", change.Text);
com.Parameters.AddWithValue("#id", id.Text);
com.ExecuteNonQuery();
}
}
MessageBox.Show("Saved");
}
catch (Exception ex)
{
MessageBox.Show("Not saved: " + ex.Message);
}
Any help would be much appreciated.
As Alex mentioned, SET part needs , instead of AND for multiple columns.
Check UPDATE syntax1;
UPDATE table_name
SET column1=value1,column2=value2,...
WHERE some_column=some_value;
But I wanna say a few things more;
Don't use AddWithValue as much as you can. It may generate unexpected and surprising results sometimes. Use Add method overload to specify your parameter type and it's size.
Open your connection just before you execute your command. That means, you should open your connection just before your ExecuteNonQuery line.
Based on it's name, ID column should be some numeric value instead of character. Consider to change it's type or consider to change it's column name that refers some character typed column name.
1: I know I know.. a w3school link
doubles quotes dont work so you have to type 'some value' to actually do variable comparisons when doing direct execution of SQL statements.
Problem is that now when I execute the SQL statement from ASP.NET code I dont seem to be getting any readings...I am not even getting errors :S....
I HAVE tried executing the SQL statement on its own, and it does work.
public static string testExi(string localIncidentNum)
{
try
{
string query = "SELECT TOP 1 UniqueColID From DBNAME WHERE LocalIncidentNum = #localIncidentNum ORDER BY [version] DESC";
DataTable dt = new DataTable();
SqlConnection connection = new SqlConnection(connectionStr);
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#localIncidentNum", localIncidentNum);
connection.Open();
SqlDataAdapter adp = new SqlDataAdapter(command);
adp.Fill(dt);
connection.Close();
command.Dispose();
connection.Dispose();
if (dt.Rows.Count != 0)
{
string UniqueColID = dt.Rows[0]["UniqueColID"].ToString();
return UniqueColID;
}
else
{
return null;
}
string some = dt.Rows[0]["UniqueColID"].ToString();
return some;
}
catch (Exception err)
{
Global.tmpmsg = " Updating follow up was not successful. " + err.ToString();
return null;
}
}
If I hardcode an incident value in the SELECT statement it works but if I hardcode the incident value in .addwithvalue, it doesn't work.
command.Parameters.AddWithValue("#localIncidentNum", "12-023696");
Double check your sql statement:
SELECT TOP 1 UniqueColID From WHERE LocalIncidentNum = #localIncidentNum ORDER BY [version] DESC
From Where?
Edit
In observance of your change, best to always be as accurate as possible when describing your problem. Leaving out something like the table name of a sql statement is very misleading.
Perhaps add a datatype to your command parameter. I believe that you are not getting anything because it may be timing out on the command.
command.Parameters.AddWithValue("#localIncidentNum", localIncidentNum);
command.Parameters[0].SqlDbType = SqlDbType.VarChar;
I found a similar problem here, also using Varchar:
AddWithValue without DBType causing queries to run slowly
I solved it. The problem was that I (for some reason) needed to put the full path of the table before the table name in sql code when executing it from C sharp file:
SELECT TOP 2 [DB name].[dbo]. [table name]