SQL Transaction not committing to database - c#

I am writing my first SQL transaction code and have scoured stackoverflow to make sure I've done it right. The basic idea of the project is to get an email, parse it into a RawDatum object, then write the contents of the object to the SQL database. I was hoping to commit the new rows in batches of 100.
I have the code running without errors, but nothing is being actually written to the database.
I have a local DB (MS SQL Server Express). I have a few things commented out for testing purposes. What am I doing wrong?
using (SqlConnection connection = new SqlConnection(Properties.Settings.Default.MissionMetricsConnectionString))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
command.CommandType = CommandType.Text;
command.CommandText = #"INSERT INTO RawData (MessageUID, DateTime, Attribute, DigitalValue, AnalogValue) VALUES (#MessageUID, #DateTime, #Attribute, #DigitalValue, #AnalogValue)";
command.Parameters.Add("#MessageUID", SqlDbType.Int);
command.Parameters.Add("#DateTime", SqlDbType.DateTime);
command.Parameters.Add("#Attribute", SqlDbType.Text);
command.Parameters.Add("#DigitalValue", SqlDbType.Bit);
command.Parameters.Add("#AnalogValue", SqlDbType.Float);
command.Connection = connection;
RawDatum currentDatum = null;
int uid;
List<int> uidsToDelete = new List<int>();
int numLoops = 1;//(int)(ids.Length / loopSize) + 1;
for (var loopIndex = 0; loopIndex < numLoops; loopIndex++)
{
int startIndex = loopIndex * loopSize;
int endIndex = 10;// Math.Min((loopIndex + 1) * loopSize, ids.Length);
using (SqlTransaction transaction = connection.BeginTransaction())
{
try
{
command.Transaction = transaction;
for (int i = startIndex; i < endIndex; i++)
{
msg = inbox.Fetch.MessageObject(ids[i]);
uid = inbox.Fetch.Uid(ids[i]);
if (msg.Subject == "[METRICS]")
{
currentDatum = new RawDatum(uid, msg.Date, msg.BodyText.TextStripped);
command.Parameters["#MessageUID"].Value = currentDatum.MessageUid;
command.Parameters["#DateTime"].Value = currentDatum.DateTime;
command.Parameters["#Attribute"].Value = currentDatum.Attribute;
command.Parameters["#DigitalValue"].Value = currentDatum.DigitalValue;
command.Parameters["#AnalogValue"].Value = currentDatum.AnalogValue;
int queryResult = command.ExecuteNonQuery();
if (queryResult != 1)
{
throw new InvalidProgramException();
}
}
uidsToDelete.Add(uid);
}
transaction.Commit();
//Delete(uidsToDelete);
output[1] += uidsToDelete.Count;
uidsToDelete.Clear();
}
catch
{
transaction.Rollback();
output[0] += 1;
output[2] += endIndex - startIndex;
}
}
}
connection.Close();
}

Related

C# 'dbConn.ServerVersion' threw an exception of type 'System.InvalidOperationException'

Have the error 'dbConn.ServerVersion' threw an exception of type 'System.InvalidOperationException, however VisualStudio does not pause the program and throw the exception at me. Here is the code:private void BTN_NA_Click(object sender, EventArgs e)
{
if (TXT_NAUN.Text != "" && TXT_NAPW.Text != "" && TXT_NAPW2.Text != "")
{
if (TXT_NAPW.Text == TXT_NAPW2.Text)
{
string input = TXT_NAPW.Text;
int hash = 0;
int output = 0;
foreach (char chara in input)
{
int temp = 0;
temp = System.Convert.ToInt32(chara);
output = output + temp;
output = output * 2;
}
hash = output % 1000;
OleDbConnection dbConn = new System.Data.OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=BHShooterProjectDB.accdb");
string sql = "SELECT Accounts.PlayerID FROM Accounts ORDER BY Accounts.PlayerID DESC ";
///string comm = "SELECT Accounts.PlayerID from Accounts";
/// INNER JOIN Leaderboard ON Leaderboard.PlayerID = Accounts.PlayerID WHERE Accounts.PlayerUsername = #ip";
OleDbCommand cmd = new OleDbCommand(sql, dbConn);
string result = "";
dbConn.Open();
OleDbDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
result = reader[0].ToString();
}
dbConn.Close();
{
string comm = "INSERT INTO Accounts (PlayerUsername, PlayerPassword, PlayerID, PlayerInvID) VALUES (#NAUN, #HPW, #NAPI, #NAPI)";
OleDbCommand command = new OleDbCommand(comm, dbConn);
command.Parameters.AddWithValue("#NAUN", TXT_NAUN.Text);
command.Parameters.AddWithValue("#HPW", hash);
foreach (char chara in result)
{
int temp = 0;
temp = System.Convert.ToInt32(chara);
result = result + temp;
}
result = result + 1;
command.Parameters.AddWithValue("#NAPI", result);
command.Parameters.AddWithValue("#NAPI", result);
dbConn.Open();
int rowsAffected = cmd.ExecuteNonQuery(); ///error appears here
dbConn.Close();
}
}
}
}
Any suggestions on solution, have tried a lot and this is my last hope!
Thanks,
Blackclaw_
At the line you got the error, you are using cmd (the select command). I think you want to use command (the insert command).

How to use counter with odbc transaction in C#

How to implement counter, so that it commit multiple queries, for example every 1000 "queries". Problem is that with or without transaction query is executed by "ExecuteNonQuery()" and it execute one by one, not as I whant e.g. every 1000?
odbc.dbsqlite.Open();
odbc.dbkopito.Open();
OdbcCommand comlite = odbc.dbsqlite.CreateCommand();
OdbcCommand comkopit = odbc.dbkopito.CreateCommand();
OdbcTransaction transaction = null;
comkopit.CommandText =
"SELECT DISTINCT ... "
#region TRY
try
{
OdbcDataReader dbKopitReader = comkopit.ExecuteReader();
var ordinal = new
{
cenik = dbKopitReader.GetOrdinal("sifra"),
ident = dbKopitReader.GetOrdinal("ident"),
klasifikacija = dbKopitReader.GetOrdinal("klasifikacija"),
cena = dbKopitReader.GetOrdinal("cena"),
eankoda = dbKopitReader.GetOrdinal("eankoda"),
};
int stevec = 0;
while (dbKopitReader.Read())
{
var cena = Convert.ToDouble(dbKopitReader.GetDouble(ordinal.cena));
var ident = Convert.ToString(dbKopitReader.GetString(ordinal.ident));
var cenik = Convert.ToString(dbKopitReader.GetString(ordinal.cenik));
var klasi = Convert.ToString(dbKopitReader.GetString(ordinal.klasifikacija));
var eanko = Convert.ToString(dbKopitReader.GetString(ordinal.eankoda));
using (var cmd = odbc.dbsqlite.CreateCommand() )
{
try
{
cmd.CommandText = "INSERT OR REPLACE INTO ARTIKLI (KLASIFIKACIJA, CENA, BARKODA, CENIK, IDENT) " +
"VALUES (?,?,?,?,?);";
cmd.Parameters.AddWithValue("#KLASIFIKACIJA", klasi);
cmd.Parameters.AddWithValue("#CENA", cena);
cmd.Parameters.AddWithValue("#BARKODA", eanko);
cmd.Parameters.AddWithValue("#CENIK", cenik);
cmd.Parameters.AddWithValue("#IDENT", ident);
cmd.ExecuteNonQuery();
if (stevec % 1000 == 0)
{
transaction.Commit();
transaction = odbc.dbsqlite.BeginTransaction();
cmd.Transaction = transaction;
}
stevec++;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
try
{
transaction.Rollback();
}
catch
{
Console.WriteLine("Transakcija ni aktivna");
}
}
}
}
comlite.Dispose();
odbc.dbsqlite.Close();
dbKopitReader.Close();
comkopit.Dispose();
odbc.dbkopito.Close();
Transaction is not initiated at first iteration (should get null exception).
Transaction is not assigned to command.
You creating transaction and assigning it to command every 1000th, but next 999 commands is created without transactions.
You committing transaction on very first iteration.
Here is example how your code should look like:
var transaction = odbc.dbsqlite.BeginTransaction();
int count = 0;
while(read)
{
count++;
if(count % 1000 == 0)
{
transaction.Commit();
transaction = odbc.dbsqlite.BeginTransaction();
}
//I imagine this CreateCommand() is assigning connection...
using(var cmd = odbc.dbsqlite.CreateCommand())
{
cmd.CommandText = "INSERT...";
//Params...
cmd.Transaction = transaction;
cmd.ExecuteNonQuery();
}
}

Ta-Lib ATR function always returns 0

I am using the Ta-Lib in C# and for the most part it works great. I am using it to calculate a number of moving averages and they calculate perfectly.
When Calling the ATR function it returns 0, always. I use the same parameters (the ones that overlap) as I do for the SMA, but still 0. Here is the code in question. Member variable Period is an integer. I have more data in the queues than the period I am asking it to calculate, and my data in the queues is clean, I've triple checked that. The return code is Success, but 0 return value.
int outbegldx = 0;
int outnbelement = 0;
Queue<double> inputHigh = new Queue<double>();
Queue<double> inputLow = new Queue<double>();
Queue<double> inputClose = new Queue<double>();
//This is in a loop
inputHigh.Enqueue(Convert.ToDouble(rdr.GetDecimal(5)));
inputLow.Enqueue(Convert.ToDouble(rdr.GetDecimal(6)));
inputClose.Enqueue(Convert.ToDouble(rdr.GetDecimal(7)));
double[] outreal = new double[inputHigh.Count];
retCode = TicTacTec.TA.Library.Core.Atr(0, inputHigh.Count - 1, inputHigh.ToArray(), inputLow.ToArray(), inputClose.ToArray(), this.Period, out outbegldx, out outnbelement, outreal);
if (retCode == Core.RetCode.Success)
{
this.Output = new AverageTrueRangeOutput(this.Period, outreal[0], outbegldx, outnbelement);
}
OK I figured it out, finally after many, many hours. In my loop I was checking to make sure the array had the same amount of elements as the Period, if so, I performed the calculation. In fact, the array needs Period + 1 elements to perform the calculation. Here is my entire function.
public override void Calculate()
{
int outbegldx = 0;
int outnbelement = 0;
int marketID = 0;
TicTacTec.TA.Library.Core.RetCode retCode;
//Queue<OHLC> input = new Queue<OHLC>();
Queue<double> inputHigh = new Queue<double>();
Queue<double> inputLow = new Queue<double>();
Queue<double> inputClose = new Queue<double>();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.DB_CONNECTION_STRING))
{
using (SqlCommand cmd = new SqlCommand("[Data].[proc_GetHistoricalData]", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#StartIndex", SqlDbType.Int);
cmd.Parameters.Add("#EndIndex", SqlDbType.Int);
cmd.Parameters.Add("#Weekly", SqlDbType.Bit);
cmd.Parameters["#StartIndex"].Value = this.startIndex;
cmd.Parameters["#EndIndex"].Value = this.endIndex;
cmd.Parameters["#Weekly"].Value = (int)this.TimeFrame;
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine(rdr.GetInt64(0));
if (marketID != rdr.GetInt32(2))
{
marketID = rdr.GetInt32(2);
inputHigh.Clear();
inputLow.Clear();
inputClose.Clear();
}
//input.Enqueue(new OHLC(rdr.GetDecimal(4), rdr.GetDecimal(5), rdr.GetDecimal(6), rdr.GetDecimal(7)));
inputHigh.Enqueue(Convert.ToDouble(rdr.GetDecimal(5)));
inputLow.Enqueue(Convert.ToDouble(rdr.GetDecimal(6)));
inputClose.Enqueue(Convert.ToDouble(rdr.GetDecimal(7)));
//We have enough data to calculate the ATR so do so
if (inputHigh.Count >= this.Period + 1)
{
double[] outreal = new double[inputHigh.Count - 1];
int lookBack = Core.AtrLookback(this.Period);
retCode = Core.Atr(0, inputHigh.Count - 1, inputHigh.ToArray(), inputLow.ToArray(), inputClose.ToArray(), lookBack, out outbegldx, out outnbelement, outreal);
//Calculated the ATR, now write it to the database
if (retCode == Core.RetCode.Success)
{
this.Output = new AverageTrueRangeOutput(this.Period, outreal[0], outbegldx, outnbelement);
//Now insert into db
using (SqlCommand cmdInsert = new SqlCommand("[Data].[proc_InsertHistoricalIndicator]", conn))
{
cmdInsert.CommandType = CommandType.StoredProcedure;
cmdInsert.Parameters.Add("#MarketID", SqlDbType.Int);
cmdInsert.Parameters.Add("#IndicatorID", SqlDbType.Int);
cmdInsert.Parameters.Add("#Date", SqlDbType.Date);
cmdInsert.Parameters.Add("#Value", SqlDbType.Xml);
cmdInsert.Parameters.Add("#Weekly", SqlDbType.Bit);
cmdInsert.Parameters["#MarketID"].Value = rdr.GetInt32(2);
cmdInsert.Parameters["#IndicatorID"].Value = this.Id;
cmdInsert.Parameters["#Weekly"].Value = (int)this.TimeFrame;
cmdInsert.Parameters["#Date"].Value = rdr.GetDateTime(3); //IBLib.Util.GetDate(
cmdInsert.Parameters["#Value"].Value = this.Output.toXML().OuterXml;
cmdInsert.ExecuteNonQuery();
}
}
inputHigh.Dequeue();
inputLow.Dequeue();
inputClose.Dequeue();
}
}
rdr.Close();
}
}
}
}
The line in question is this one - "if (inputHigh.Count >= this.Period + 1)" whereas previously I just had "if (inputHigh.Count >= this.Period)"

How to execute a ExecuteNonQuery with List parameters in Where Clause

My code looks like this:
var settings = ConfigurationManager.ConnectionStrings["InsurableRiskDB"];
string server = ConfigurationManager.AppSettings["Server"];
string cs = String.Format(ConfigurationManager.AppSettings[settings.ProviderName], ConfigurationManager.AppSettings[server]);
SqlConnection connection = new SqlConnection(cs);
string PolicyKeys = "";
for (int i = 0; i < keys.Count(); i++)
{
if (i == keys.Count() - 1)
PolicyKeys += keys[i] ;
else
PolicyKeys += keys[i] + ", ";
}
//have to change the code to pull the user's NBK.
string user = "'DATALOAD'";
const string updateQuery = #"UPDATE [InsurableRisk].[dbo].[Policy]
SET [InsuranceCarrierKey] = #ToKey
,[AuditUser] = #User
,[AuditDate] = SYSDATETIME()
WHERE PolicyKey in (#PolicyKeys) and InsuranceCarrierKey = #FromKey";
using (connection)
{
using (SqlCommand dataCommand = new SqlCommand(updateQuery, connection))
{
dataCommand.Parameters.AddWithValue("#ToKey", toKey);
dataCommand.Parameters.AddWithValue("#User", user);
dataCommand.Parameters.AddWithValue("#PolicyKeys", PolicyKeys);
dataCommand.Parameters.AddWithValue("#FromKey", fromKey);
connection.Open();
dataCommand.ExecuteNonQuery();
connection.Close();
}
}
res = true;
}
catch (Exception ex)
{
MessageBox.Show("There is an error while try in save the changes " + ex.Message, "Error Message", MessageBoxButtons.OKCancel);
res = false;
}
return res;
Now, when i run this code, it says query is unable to execute. It throws and exception stating, it is unable to convert NVarchar to int for variable #PolicyKeys
Any suggestions as to what i am missing in this code?
Typically, in SQL you'd write an IN statement like this:
WHERE SomeColumn IN ('A', 'B', 'C')
What you're doing is the equivalent of this in SQL (which won't work):
WHERE SomeColumn IN ('A, B, C')
Change your SQL statement accordingly: (modified from this answer)
WHERE PolicyKey in ({0})
And then add your parameters in a loop, like this:
var parameters = new string[keys.Count()];
for (int i = 0; i < keys.Count(); i++)
{
parameters[i] = string.Format("#Key{0}", i);
cmd.Parameters.AddWithValue(parameters[i], keys[i]);
}
cmd.CommandText = string.Format(updateQuery, string.Join(", ", parameters));

How I do Update in SQL with c#

I have this function in c#. When the FOR is call, a error appears in ExecuteNonQuery. The error is "ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized."
SqlConnection cnn = new SqlConnection(WebConfigurationManager.ConnectionStrings["strCnn"].ToString());
cnn.Open();
SqlTransaction trx = cnn.BeginTransaction();
try
{
SqlCommand cmd= new SqlCommand();
for (int j = 0; j < arr.Length; j++) {
cmd.CommandText = "UPDATE rc SET nc= " + arr[j].Col3 + " WHERE cr = " + arr[j].Col1;
cmd.Connection = cnn;
cmd.ExecuteNonQuery();
}
trx.Commit();
return 1;
}
catch (SqlException ex)
{
try
{
trx.Rollback();
return 0;
}
catch (Exception exRollback)
{
return 0;
}
}
This error message shows that you have opened a transaction and it is still open at the point of executing ExecuteNonQuery,
You are executing ExecuteNonQuery before commitment of transaction.
Define
comando.Transaction = trx;
So that ExecuteNonQuery will be executed on the same transaction.
Use
// Create command on transaction and automatically assign open transaction
var comando = conexao.CreateCommand()
or assign the transaction to the command.
// Create command
var comando = new SqlCommand();
// and assign transaction manually
comando.Transaction = trx;
you forgot to set transaction
comando.Transaction = trx;
You need to assign the transaction to the command, like so:
SqlCommand comando = new SqlCommand();
comando.Transaction = trx;
I would also recommend setting the Connection property outside of the for loop too, so you code would look like this:
SqlCommand comando = new SqlCommand();
comando.Transaction = trx;
comando.Connection = conexao;
for (int j = 0; j < arr.Length; j++) {
comando.CommandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = " + arr[j].Col3 + " WHERE CD_RECURSO = " + arr[j].Col1;
comando.ExecuteNonQuery();
}
trx.Commit();
You need to set SqlCommand's transaction property.
SqlCommand comando = new SqlCommand();
comando.Transaction = trx;
Your sqlCommand doesn't know about your transaction.
Here's a copy-paste fix:
SqlConnection conexao = new SqlConnection(WebConfigurationManager.ConnectionStrings["strConexao"].ToString());
conexao.Open();
SqlTransaction trx = conexao.BeginTransaction();
try
{
for (int j = 0; j < arr.Length; j++) {
var commandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = " + arr[j].Col3 + " WHERE CD_RECURSO = " + arr[j].Col1;
SqlCommand comando = new SqlCommand(commandText, conexao, trx);
comando.ExecuteNonQuery();
}
trx.Commit();
return 1;
}
catch (SqlException ex)
{
try
{
trx.Rollback();
return 0;
}
catch (Exception exRollback)
{
return 0;
}
}
As has been pointed out, you never assign your transaction to the command. However there are a few other points I have picked up on.
First and foremost USE PARAMETRISED QUERIES, they will improve performace, type safety and most importantly save you from SQL Injection Attacks.
So instead of:
comando.CommandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = " + arr[j].Col3 + " WHERE CD_RECURSO = " + arr[j].Col1;
You would use:
comando.CommandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = #Col3 WHERE CD_RECURSO = #Col1";
comando.Parameters.AddWithValue("#Col3", arr[j].Col3);
comando.Parameters.AddWithValue("#Col1", arr[j].Col1);
Secondly, wrap your sql command object with a using wrapper to ensure it is properly dispose of, there is no benefit from reusing the same object over and over again (and this can cause problems):
for (int j = 0; j < arr.Length; j++)
{
using (var comando = new SqlCommand("UPDATE RECURSO_CLIENTE SET NM_CLIENTE = #Col3 WHERE CD_RECURSO = #Col1", conexao))
{
comando.Transaction = trx;
comando.Parameters.AddWithValue("#Col3", arr[j].Col3);
comando.Parameters.AddWithValue("#Col1", arr[j].Col1);
comando.ExecuteNonQuery();
}
}
Finally, if you are using SQL-Server 2008+ you can use Table valued Parameters to do this update in a single query:
You first need a type
CREATE TABLE YourTypeName AS TABLE (Col1 INT, Col3 INT);
Then your update statement would be something like:
DECLARE #UpdateValues AS YourTypeName;
MERGE RECURSO_CLIENTE rc
USING #UpdateValues u
ON rc.CD_RECURSO = u.Col1
WHEN MATCHED UPDATE
SET NM_CLIENTE = u.Col3;
This means a single statement and you don't need to use explicit transactions. (You might wonder why I have used merge instead of UPDATE, here is why). So putting it all together you would get:
var dataTable = new DataTable();
dataTable.Columns.Add("Col1", typeof(int));
dataTable.Columns.Add("Col3", typeof(int));
for (int j = 0; j < arr.Length; j++)
{
var newRow = dataTable.NewRow();
newRow[0] = arr[j].Col1;
newRow[1] = arr[j].Col3;
dataTable.Rows.Add(newRow);
}
string sql = #" MERGE RECURSO_CLIENTE rc
USING #UpdateValues u
ON rc.CD_RECURSO = u.Col1
WHEN MATCHED UPDATE
SET NM_CLIENTE = u.Col3;";
using (var conexao = new SqlConnection(WebConfigurationManager.ConnectionStrings["strConexao"].ToString()))
using (var comando = new SqlCommand(sql, conexao))
{
conexao.Open();
var tableParam = new SqlParameter("#UpdateValues", SqlDbType.Structured);
tableParam.TypeName = "#YourTypeName";
tableParam.Value = dataTable;
comando.Parameters.Add(tableParam);
comando.ExecuteNonQuery();
}

Categories

Resources