Database is locked SQLite dlls for C# - c#

I have this code below:
public Dictionary<long, List<long>> CloneGroupMeasuredOutputs(ISimulation clonedSimulation, Dictionary<long, long> crashDict, Dictionary<long, long> outputDict, string variationIDs, ProgressInterface progressInterface)
{
{
Dictionary<long, List<long>> measuredOutputIndexes = new Dictionary<long, List<long>>();
DbDataReader reader = null;
long counter = 0;
string crashText = GetKeysList(crashDict);
string outputText = GetKeysList(outputDict);
int numberOfIterations = 50000;
//
var conn = ((RDB1Connection)this.DbConnection).RDB1DbConnection;
try
{
if (conn.State == ConnectionState.Closed) conn.Open();
DbCommand dbCommand = conn.CreateCommand();
dbCommand.Connection = conn;
dbCommand.CommandText = string.Format("CREATE INDEX IF NOT EXISTS MOIndexSCOVI ON MeasuredOutputs (SimulationId, CrashId, OutputId, VariationIndex)");
dbCommand.ExecuteNonQuery();
dbCommand.CommandText = string.Format(_sqlGetNumberOfSomeMeasuredOutputs, this.ID, crashText, outputText, variationIDs);
if (conn.State == ConnectionState.Closed) conn.Open();
long numberOfRows = (long)dbCommand.ExecuteScalar();
float step = (float)40.0 / (numberOfRows / numberOfIterations + 1);
dbCommand.CommandText = string.Format(_sqlGetSomeMeasuredOutputs, this.ID, crashText, outputText, variationIDs);
if (conn.State == ConnectionState.Closed) conn.Open();
reader = dbCommand.ExecuteReader();
DbCommand ClonedSimulationDbCommand = ((RDB1Connection)clonedSimulation.DbConnection).DbProviderFactory.CreateCommand();
ClonedSimulationDbCommand.Connection = ((RDB1Connection)clonedSimulation.DbConnection).RDB1DbConnection;
if (clonedSimulation.IsConnectionClosed())
clonedSimulation.OpenConnection();
long _id;
List<long> measuredOutputsIds = null;
while (reader.Read())
{
var p0 = (double)reader["value"];
var p1 = (long)reader["runIndex"];
var p2 = crashDict[(long)reader["crashId"]];
var p3 = outputDict[(long)reader["outputId"]];
var p4 = (long)reader["variationIndex"];
ClonedSimulationDbCommand.CommandText = string.Format(_sqlInsertRowIntoMeasuredOutputs, p0, p1, p2, p3, p4, (long)clonedSimulation.ID);
measuredOutputsIds = new List<long>();
// p2.Value = crashId;
_id = (long)ClonedSimulationDbCommand.ExecuteScalar();
//_id = (long)dbCommand.ExecuteScalar();
measuredOutputsIds.Add(_id);
counter++;
measuredOutputIndexes.Add((long)reader["id"], measuredOutputsIds);
if (counter >= numberOfIterations)
{
counter = 0;
((RDB1Connection)clonedSimulation.DbConnection).CommitTransaction();
if (progressInterface.isCancelled())
{
reader.Close();
return null;
}
else
{
progressInterface.updateProgressWith(step);
}
//((RDB1Connection)clonedSimulation.DbConnection).BeginTransaction();
}
}
reader.Close();
((RDB1Connection)clonedSimulation.DbConnection).CommitTransaction();
return measuredOutputIndexes;
}
catch (Exception ex)
{
((RDB1Connection)clonedSimulation.DbConnection).RollbackTransaction();
throw ex;
}
finally
{
if (reader != null)
reader.Close();
}
}
}
At this line: _id = (long)ClonedSimulationDbCommand.ExecuteScalar(); i receive a database locked exception
The connection is open, the path to the database exist and it is valid. I think that the fact that I open to connections for the same database might be the problem.
The SQL statement is:
_sqlInsertRowIntoMeasuredOutputs = #"INSERT INTO MeasuredOutputs (Value,RunIndex,CrashId,OutputId,VariationIndex,SimulationId) VALUES({0},{1},{2},{3},{4},{5}); SELECT last_insert_rowid()";
Thank you!

It is likely because you are trying to execute another command on the connection when that connection is still being read via the DataReader.
You can not use a connection for executing more commands while the datareader still has more data available to it (unless the database supports Multiple Active Result Sets -- MARS), which Sqlite probably doesn't. That means you must either read the entire first result before executing another command, or use a different connection to do so.

Related

invalid attempt to call read when READER IS CLOSED .. error repeat at every line

I am getting this error while i am trying to retrieve data from table.,
I am getting error at every line in while(r1.read()) loop., if i remove that line then i am getting same error in next line, it continues till last line of loop..
Please help.,
void otherdata()
{
if (conn.State == ConnectionState.Closed) conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "select customer.cust_name,customer.cust_id,purchase_details.purchase_id,purchase_details.pdate,purchase_details.subtotal,purchase_details.total,purchase_details.supp_bill_id from Purchase_details inner join customer on purchase_details.supplier_id=customer.cust_id where id =" + pd;
SqlDataReader r1 = cmd.ExecuteReader();
while (r1.Read())
{
cbsupplier.Text = r1["cust_name"].ToString();
txtdate.Value = Convert.ToDateTime(r1["pdate"]);
txtsubtotal.Text = r1["subtotal"].ToString();
custid.Text = r1["cust_id"].ToString();
MessageBox.Show("pochalo");
}
r1.Close();
conn.Close();
if (conn.State == ConnectionState.Closed) conn.Open();
cmd.CommandText = "select * from purchase where purchase_id =" + pd;
r1 = cmd.ExecuteReader();
while (r1.Read())
{
txtadjust.Text = r1["adjustment"].ToString();
cbtaxrate.Text = r["taxrate"].ToString();
txttax.Text = r["tax"].ToString();
}
conn.Close();
r1.Close();
}
for your exception, i'm guessing is throwing this exception at this line :
cbtaxrate.Text = r["taxrate"].ToString();
txttax.Text = r["tax"].ToString();
because r["taxrate"] is not the same reader as r1["adjustment"]. So, i'm guessing you opened a reader with a name r in the same scope, then closed it, and opened a new reader r1 and did the above code. the issue here is not about closing the reader connection, it's about following the best practices in handling instances and naming conversion. if you chose a readable naming for your variables, it will be easer for you to read and follow the code.
You need also to use using clause on disposable objects, for a better disposal handling.
Here is a helper method that would saves you the hassle:
public static class SqlConnectionHelper
{
public static IEnumerable<KeyValuePair<string , object>> GetQueryValues(this SqlConnection connection , string sql , SqlParameter[] parameters , string[] fields)
{
if(connection == null)
{
throw new ArgumentNullException(nameof(connection));
}
var mappedFields = new Dictionary<int, string>();
using(var command = new SqlCommand(sql , connection))
{
if(parameters?.Length > 0)
{
command.Parameters.AddRange(parameters);
}
if(connection.State == ConnectionState.Closed)
connection.Open();
using(var reader = command.ExecuteReader())
{
if(reader.HasRows)
yield break;
if(fields?.Length > 0)
{
for(int x = 0; x < reader.FieldCount; x++)
{
var columnName = reader.GetName(x);
if(Array.Exists(fields , i => i.Equals(columnName , StringComparison.OrdinalIgnoreCase)))
{
mappedFields.Add(x , columnName);
}
}
while(reader.Read())
{
foreach(var field in mappedFields)
{
yield return new KeyValuePair<string , object>(field.Value , reader.GetValue(field.Key));
}
}
}
else
{
while(reader.Read())
{
for(int x = 0; x < reader.FieldCount; x++)
{
yield return new KeyValuePair<string , object>(reader.GetName(x) , reader.GetValue(x));
}
}
}
}
}
}
public static Dictionary<string , object> GetQueryUniqueValues(this SqlConnection connection , string sql , SqlParameter[] parameters , string[] fields)
{
return GetQueryValues(connection , sql , parameters , fields)
.GroupBy(x => x.Key)
.Select(x => new
{
Name = x.Key ,
Value = x.FirstOrDefault(s => s.Key == x.Key).Value
})
.ToDictionary(x => x.Name , x => x.Value);
}
}
with the reader helper, you can provide fields to get selected columns values, or you can just pass null to it and it will read all columns that have been provided under the SELECT statement.
reusing this to your current code would be like :
using(var connection = new SqlConnection(connectionString))
{
const string purchaseDetailsSql = "SELECT c.cust_name, c.cust_id, p.purchase_id, p.pdate, p.subtotal, p.total, p.supp_bill_id FROM Purchase_details p INNER JOIN customer c ON p.supplier_id = c.cust_id WHERE id = #id ;";
const string purchasesSql = "SELECT adjustment, taxrate, tax FROM purchase WHERE purchase_id = #id ;";
var parameters = new[]
{
new SqlParameter("#id", pd)
{
SqlDbType = SqlDbType.Int
}
};
var fields = new[] { "cust_name", "pdate" , "subtotal", "cust_id" };
// here would select the columns from the provided fields array.
var purchaseDetails = connection.GetQueryUniqueValues(purchaseDetailsSql , parameters, new[] { "cust_name", "pdate" , "subtotal", "cust_id" });
// here would select the columns from the query.
var purchases = connection.GetQueryUniqueValues(purchasesSql, parameters, null);
cbsupplier.Text = purchaseDetails["cust_name"]?.ToString();
txtdate.Value = Convert.ToDateTime(purchaseDetails["pdate"]);
txtsubtotal.Text = purchaseDetails["subtotal"]?.ToString();
custid.Text = purchaseDetails["cust_id"]?.ToString();
MessageBox.Show("pochalo");
txtadjust.Text = purchases["adjustment"]?.ToString();
cbtaxrate.Text = purchases["taxrate"]?.ToString();
txttax.Text = purchases["tax"]?.ToString();
}

How to get data in INT from SQLDataReader in .NET

I am trying to get int value from the database but It is throwing an error
Unable to cast object of type 'System.Byte' to type 'System.Int32'.
In the database, Active field is tinyint.
Also, how to return both values from this method.
private string CheckData(string firstValue, string SecondValue, int Active)
{
string Data = "";
StringBuilder sb = new StringBuilder();
string query = #"select M.ident Mi, mmp.active Active
from Iden.Iden M
inner join PtM.MPt MMP on MMP.mPat = M.id
where M.ident = 'firstValue'
and Mi.ident = 'SecondValue'";
sb.Append(query);
sb.Replace("firstValue", firstValue);
sb.Replace("SecondValue", SecondValue);
SqlConnection connection = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(sb.ToString());
cmd.CommandTimeout = 0;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
try
{
connection.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Data = reader.GetString(reader.GetOrdinal("Mi"));
Active = reader.GetInt32(reader.GetOrdinal("Active"));
}
}
}
catch (Exception ex)
{
_log.Error($"Exception:{ex.Message}");
}
finally
{
connection.Close();
connection.Dispose();
}
return Data;
}
Here's a stab at it. I can't debug it (since I don't feel like creating a database).
First I create a type to hold the results. You could just use a Tuple, but this seems clearer:
public class DataActive
{
public string Data { get; set; }
public byte Active { get; set; }
}
I make your function return a collection of these - it's not obvious from your code that there is only one.
You'll also notice that I use SqlParameters to add firstValue and secondValue to your query. Look up SQL Injection (and Little Bobby Tables).
If you are using a recent version of C# (which I don't), there's a new syntax for using that requires less indenting. The using statements stick a call to Dispose in a finally statement at the end of the block. Also note that I'm disposing the SqlCommand and the DataReader
public static IEnumerable<DataActive> CheckData(string firstValue, string secondValue)
{
var results = new List<DataActive>();
const string query = #"select M.ident Mi,mmp.active Active from Iden.Iden M
Inner join PtM.MPt MMP on MMP.mPat =M.id
where M.ident = #firstValue and Mi.ident = #secondValue";
using (var connection = new SqlConnection(connString))
{
using (var cmd = new SqlCommand(query))
{
cmd.CommandTimeout = 0;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
cmd.Parameters.Add("#firstValue", SqlDbType.NVarChar, 50).Value = firstValue;
cmd.Parameters.Add("#secondValue", SqlDbType.NVarChar, 50).Value = secondValue;
try
{
connection.Open();
using (var reader = cmd.ExecuteReader())
{
var dataOrdinal = reader.GetOrdinal("Mi");
var activeOrdinal = reader.GetOrdinal("Active");
if (reader.HasRows)
{
while (reader.Read())
{
results.Add(new DataActive
{
Data = reader.GetString(dataOrdinal),
Active = reader.GetByte(activeOrdinal),
});
}
}
}
}
catch (Exception ex)
{
_log.Error($"Exception:{ex.Message}");
}
}
}
return results;
}
If your TINY_INT Active represents a boolean value, figure out what the rule is, and do a conversion after you get the value using reader.GetByte.
One final note, it's often better to log ex.ToString() rather than ex.Message. You get the message and the stack that way.

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();
}
}

SQL Transaction not committing to database

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();
}

Categories

Resources