I want to convert my retrieved data into JSON; I am using a SqlDataReader for retrieving. For that I need to store my data into var.
I am getting this error:
An exception of type 'System.Reflection.AmbiguousMatchException' occurred in mscorlib.dll but was not handled in user code
Additional information: Ambiguous match found.
in the following code:
public string GetDetails(int Id)
{
var jsonDoc = "";
string con = "server=FACULTY01\\SQLSERVER2012ENT; database=SampleDb; uid=sa; pwd=sa9";
SqlConnection scon = new SqlConnection(con);
string qry = "Select * from Information where ID =" + Id;
SqlCommand cmd = new SqlCommand(qry, scon);
scon.Open();
SqlDataReader rdr = cmd.ExecuteReader();
var details = rdr;
JavaScriptSerializer jss = new JavaScriptSerializer();
jsonDoc = jss.Serialize(details);
scon.Close();
return jsonDoc;
}
You can't directly serialize a SqlDataReader and expect the output to contain the data from the SQL query. That's not the way that SqlDataReader works. SqlDataReader is a means to retrieve data from the SQL result. You can use it to populate some other object (such as a Dictionary<string, object> or a strongly-typed class you define) which you can then hand to the serializer to produce JSON.
Try the code below instead. (Also note the use of using statements and parameterized SQL in keeping with good coding practices, as mentioned by #Jon Skeet.)
public static string GetDetails(int Id)
{
string con = "server=FACULTY01\\SQLSERVER2012ENT; database=SampleDb; uid=sa; pwd=sa9";
using (SqlConnection scon = new SqlConnection(con))
{
string qry = "Select * from Information where ID = #id";
SqlCommand cmd = new SqlCommand(qry, scon);
cmd.Parameters.AddWithValue("#id", Id);
scon.Open();
var details = new Dictionary<string, object>();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
if (rdr.HasRows && rdr.Read())
{
for (int i = 0; i < rdr.FieldCount; i++)
{
details.Add(rdr.GetName(i), rdr.IsDBNull(i) ? null : rdr.GetValue(i));
}
}
}
JavaScriptSerializer jss = new JavaScriptSerializer();
string jsonDoc = jss.Serialize(details);
scon.Close();
return jsonDoc;
}
}
Note that the above code is expecting to get a single row back from the reader. If you are expecting more than one row then you will need to use another loop and put the resulting data into a List<Dictionary<string, object>> instead. Here is the part you would need to change:
...
var details = new List<Dictionary<string, object>>();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
if (rdr.HasRows)
{
while (rdr.Read())
{
var dict = new Dictionary<string, object>();
for (int i = 0; i < rdr.FieldCount; i++)
{
dict.Add(rdr.GetName(i), rdr.IsDBNull(i) ? null : rdr.GetValue(i));
}
details.Add(dict);
}
}
}
...
You need a loop to store the data from the reader. Use a DataTable to store the full table. A single variable will store single column data.
while (rdr.Read())
{
var details = rdr["columnName"];
}
Encountered this post as it comes first in google search.
Have given an answer here (based on this):
The requirement is somewhat similar that is of converting sqldatareader result to a json string.
public static class MyExtensions
{
public async static Task<string> toJSON(this SqlDataReader reader)
{
var results = await reader.GetSerialized();
return JsonConvert.SerializeObject(results, Formatting.Indented);
}
public async static Task<IEnumerable<Dictionary<string, object>>> GetSerialized(this SqlDataReader reader)
{
var results = new List<Dictionary<string, object>>();
var cols = new List<string>();
for (var i = 0; i < reader.FieldCount; i++)
cols.Add(reader.GetName(i));
while (await reader.ReadAsync())
results.Add(SerializeRow(cols, reader));
return results;
}
private static Dictionary<string, object> SerializeRow(IEnumerable<string> cols,
SqlDataReader reader)
{
var result = new Dictionary<string, object>();
foreach (var col in cols)
result.Add(col, reader[col]);
return result;
}
}
Use As :
var result = await reader.GetSerialized(); //to get the result object
or
string strResult = await reader.toJSON(); //to get the result string
Related
I am new to working in dot net. What I am trying to do is create a helper method which will fetch data from DB for any table. I have passed parameters dynamically it working fine. But when I try to read the data, I am finding it difficult to store the data in some collection. This I will be returning back to my calling point and bind it to a response type and return.
public static Dictionary<Dictionary<string, object>, object> GetData(SqlCommand cmd, string connectionString, List<SqlParameter> parameters)
{
try
{
SqlDataReader reader = null;
Dictionary<Dictionary<string,object>, object> returnObjects = new Dictionary<Dictionary<string, object>, object>();
Dictionary<string, object> returnObject = new Dictionary<string, object>();
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
cmd.Connection = sqlConnection;
cmd.CommandType = CommandType.StoredProcedure;
foreach (SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
returnObject.Add(reader.GetName(i), reader[i]);
}
returnObject.Clear();
returnObjects.Add(returnObject, reader);
}
}
}
return returnObjects;
}
catch (Exception ex)
{
throw;
}
}
In above code when i try to add in returnObjects dictionary it says key already added. Below is the code from where I am calling and patientResponse where I want to return.
Dictionary<Dictionary<string, object>, object> dct = new Dictionary<Dictionary<string, object>, object>();
dct = Helper.GetData(cmd, connectionString, parameters);
List<Patient_Response> pp = new List<Patient_Response>();
Patient_Response pr = new Patient_Response();
pr.Patient_Id = int.Parse(reader["ID"].ToString());
pr.FIRST_NAME = reader["FIRST_NAME"].ToString();
pr.LAST_NAME = reader["LAST_NAME"].ToString();
pr.phoneNumber = reader["TEL"].ToString();
pr.email = reader["EMAIL"].ToString();
pr.Address = reader["Address"].ToString();
pr.Gender = reader["Gender"].ToString();
pr.DOB = Convert.ToDateTime(reader["DOB"]).ToString("MM/dd/yyyy");
pp.Add(pr);
What i can use instead of dictionary so that i can get a collection returned.
What surely doesn't work in your code is that you add the same instance of returnObject for each row. You should put new Dictionary<string, object>() inside the while. Also, if your result set contains duplicate field names, you'll get an exception.
public static List<Dictionary<string, object>> GetData(SqlCommand cmd, string connectionString, List<SqlParameter> parameters)
{
try
{
SqlDataReader reader = null;
// changed this to be a list
List<Dictionary<string,object>> returnObjects = new List<Dictionary<string, object>>();
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
cmd.Connection = sqlConnection;
cmd.CommandType = CommandType.StoredProcedure;
foreach (SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
// create a new returnObject for each row
Dictionary<string, object> returnObject = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
// following line throws an exception if there are multiple fields with the same name
returnObject.Add(reader.GetName(i), reader[i]);
}
returnObjects.Add(returnObject);
}
}
}
return returnObjects;
}
catch (Exception ex)
{
throw;
}
}
That said, there is not much benefit from first putting the content of reader into a list of dictionaries and then into a list of Patient_Response. You might as well directly fill a List<Patient_Response> from the reader (i.e. inside the while).
A generic way to do this might be (code is not verified to compile or run):
public static List<T> GetData<T>(
SqlCommand cmd,
string connectionString,
List<SqlParameter> parameters,
Func<IDataReader, T> readerToRow)
{
List<T> returnObjects = new List<T>();
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
cmd.Connection = sqlConnection;
cmd.CommandType = CommandType.StoredProcedure;
foreach (SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
returnObjects.Add(readerToRow(reader));
}
}
return returnObjects;
}
// To call above function:
List<Patient_Response> pp = Helper.GetData(
cmd,
connectionString,
parameters,
reader =>
new Patient_Response
{
FIRST_NAME = reader["FIRST_NAME"].ToString(),
LAST_NAME = reader["LAST_NAME"].ToString(),
// more columns go here
}
);
Could anyone give me a step to solve this error in getting a output. New to coding.
Step 1:
Using Json/Javascript calling a method which is in .cs file(c#)
var markers = JSON.parse('<%=ConvertDataTabletoString("NewGetTaskWorkPercentage1",null) %>');
Step 2:
.cs file code for method ConvertDataTabletoString
public string ConvertDataTabletoString(string Sql, string prjno)
{
//var qprojectno = Request.QueryString["ProjectNo"].ToString();
param1.Value = "P-2020-0009"; //aspx hidden field
DataTable dt = new DataTable();
using (con = new SqlConnection(connectionString))
{
using (cmd = new SqlCommand(Sql, con))
{
string connetionString = "Data Source = ; Initial Catalog = DashBoardDB; User ID = ; Password = ";
SqlConnection connection = new SqlConnection(connetionString);
connection.Open();
string sql1 = "SELECT InstanceId,Module,SubModule,Process FROM Xtable WHERE ProjectNo=" + "'" + param1.Value + "'"+ "and instanceid='aabed4df-3e91-41c0-8788-2e7d31eecfd8'";
SqlCommand command = new SqlCommand(sql1, connection);
command.CommandTimeout = 600;
SqlDataReader dataReader = command.ExecuteReader();
serializedResult = " ";
while (dataReader.Read())
{
string strInsId = " ";
string strProcess = "";
strInsId = dataReader["InstanceId"].ToString();
con.Open();
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters.Add(new SqlParameter("#ProjectNo", prjno));
cmd.Parameters.Add(new SqlParameter("#InstanceId", strInsId));
sda = new SqlDataAdapter(cmd);
sda.Fill(dt);
System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
Dictionary<string, object> row;
foreach (DataRow dr in dt.Rows)
{
Console.WriteLine(dr + " " + dt.Rows);
row = new Dictionary<string, object>();
foreach (DataColumn col in dt.Columns)
{
row.Add(col.ColumnName, dr[col]);
}
rows.Add(row);
}
serializedResult = serializer.Serialize(rows);
serializedResult += serializer.Serialize(rows); **two times concatenating**
con.Close();}
dataReader.Close();
command.Dispose();
connection.Close();
return serializedResult;
}
}
}
Step 3:
Image Attached
In client side how to split this values into arrays. I required output like this. How can i get the output in array and the length of the array at client side. Thanks
Array 1:
[{"Task":"Feature 1.1.3","OverallSubtaskDuration":17.00,"ActualStart":"2019/10/18","ActualEnd":"2020/03/13","OverallSubtaskPercentage":283.33},
{"Task":"Feature 1.1.3","OverallSubtaskDuration":8.00,"ActualStart":"2019/10/18","ActualEnd":"2019/10/25","OverallSubtaskPercentage":133.33},
{"Task":"Feature 1.1.3","OverallSubtaskDuration":3.00,"ActualStart":"2020/03/11","ActualEnd":"2020/03/13","OverallSubtaskPercentage":50.00},
{"Task":"Feature 1.1.3","OverallSubtaskDuration":3.00,"ActualStart":"2019/10/27","ActualEnd":"2019/10/29","OverallSubtaskPercentage":50.00}]
Array 2:
[{"Task":"Feature 1.1.3","OverallSubtaskDuration":17.00,"ActualStart":"2019/10/18","ActualEnd":"2020/03/13","OverallSubtaskPercentage":283.33},
{"Task":"Feature 1.1.3","OverallSubtaskDuration":8.00,"ActualStart":"2019/10/18","ActualEnd":"2019/10/25","OverallSubtaskPercentage":133.33},
{"Task":"Feature 1.1.3","OverallSubtaskDuration":3.00,"ActualStart":"2020/03/11","ActualEnd":"2020/03/13","OverallSubtaskPercentage":50.00},
{"Task":"Feature 1.1.3","OverallSubtaskDuration":3.00,"ActualStart":"2019/10/27","ActualEnd":"2019/10/29","OverallSubtaskPercentage":50.00}]
The JSON result has to be either an object or an array.
The result you are creating is two arrays back to back and therefore not valid JSON and you can test this by using an online validator such as https://jsonlint.com/
So you either need to create one large array with all the elements from both your other arrays, or you need to wrap the two arrays into a wrapper object.
The simplest for you, as you have already are using a List<>, is to combine this with itself and return the result.
//Add the "rows" list to itself, so you double it.
rows.AddRange(rows);
serializedResult = serializer.Serialize(rows);
...
return serializedResult;
If you need to keep the lists distinct then you have to use a wrapper object. Like this;
public class Wrapper
{
public List<Dictionary<string, object>> Array1 { get; set; }
public List<Dictionary<string, object>> Array2 { get; set; }
}
Then you serialise like this
serializedResult = serializer.Serialize(new Wrapper() { Array1 = rows, Array2 = rows };);
...
return serializedResult;
You can use newtonsoft json dll to create json file from .cs, and check json string with link: https://jsoneditoronline.org/
I have put together the following method:
public static ArrayList DbQueryToArry()
{
string SqlCString = "connString";
SqlConnection connection = null;
ArrayList valuesList = new ArrayList();
connection = new SqlConnection(SqlCString);
connection.Open();
SqlCommand command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader[0]));
}
return valuesList;
}
I'd like to be able to run an assertion like this one:
var a = DbQueryToArry();
Assert.IsTrue(a.Contains("some value"));
Given reader [0]
valuesList.Add(Convert.ToString(reader[0]));
I only get the first column (CLIENTINFO) into the array and not the second (ACCOUNT_Purpose). How should I modify the code to get both ?
In addition, the returned values could be either a String or Int so would my current code version should be handling both ?
Thanks in advance.
If we switch from obsolete ArrayList to something like IEnumerable<T>:
public static IEnumerable<IDataRecord> DbQueryToArray(string sql) {
if (null == sql)
throw new ArgumentNullException(nameof(sql));
//TODO: do not hardcode connetcion string but read it (say, from Settings)
string SqlCString = "connString";
//DONE: Wrap IDisposable into using
using (SqlConnection connection = new SqlConnection(SqlCString)) {
connection.Open();
//DONE: Wrap IDisposable into using
using (SqlCommand command = new SqlCommand(sql, connection)) {
//DONE: Wrap IDisposable into using
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
yield return reader as IDataRecord;
}
}
}
}
}
then you can use Linq in order to query the result:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT");
Assert.IsTrue(a.Any(record =>
Convert.ToString(record["CLIENTNO"]) == "some value"));
Assert.IsTrue(a.Any(record =>
Convert.ToString(record["ACCOUNT_Purpose"]) == "some other value"));
If you don't want execute query several times, you can materialize the results:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT")
.ToList();
Assert.IsTrue(a.Any(record => Convert.ToString(record[0]) == "some value"));
Assert.IsTrue(a.Any(record => Convert.ToString(record[1]) == "some other value"));
Finally (see comments below), if we want to test if any field in any record has the value:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT")
.SelectMany(line => {
// Flatten the cursor into IEnumerable<String>
string[] result = new string[line.FieldCount];
for (int i = 0; i < result.Length; ++i)
result[i] = Convert.ToString(line[i]);
return result;
});
a.Any(item => item == "some value");
It is because you only read the first value of the reader. Reader.Read() read each row one by one and Convert.ToString(reader[0])) means that you want to read the first column as string.
That's cause you are getting only the first column. You can do something like below by specifying the column name
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader["CLIENTNO"]));
valuesList.Add(Convert.ToString(reader["ACCOUNT_Purpose"]));
}
Moreover, since you are converting all the columns to string; I would suggest to use a strongly typed collection like List<string> rather then ArrayList valuesList = new ArrayList();
The other answers are good, However some concerns
Lets stop using ArrayList, and use a List<T> instead
Lets use a using statement where you can
Note : I have used a ValueTuple to return more than 1 field
Example
public static List<(string clientNo, string account)> DbQueryToArray()
{
const string SqlCString = "connString";
var valuesList = new List<(string clientNo, string account)>();
using (var connection = new SqlConnection(SqlCString))
{
connection.Open();
using (var command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection))
{
var reader = command.ExecuteReader();
while (reader.Read())
valuesList.Add(((string)reader[0],(string)reader[1]) );
}
}
return valuesList;
}
Usage
var results = DbQueryToArray();
Assert.IsTrue(results.Any(x => x.clientNo == someValue || x.account == someValue));
best practice is to check first if the reader has rows
reader.HasRows
then close the reader and the connection
your code should look like this:
public static ArrayList DbQueryToArry()
{
string SqlCString = "connString";
SqlConnection connection = null;
ArrayList valuesList = new ArrayList();
connection = new SqlConnection(SqlCString);
using (connection)
{
connection.Open();
SqlCommand command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection);
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader[0]));
valuesList.Add(Convert.ToString(reader[1])); // add to valuelist
}
}
reader.Close(); // close reader
} //dispose connection
return valuesList;
}
Use DataTable and SqlDataAdapter to get query result in form of table. Something like this:
string connString = #"your connection string here";
string query = "select * from table";
DataTable dataTable = new DataTable();
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
// create data adapter
SqlDataAdapter da = new SqlDataAdapter(cmd);
// this will query your database and return the result to your datatable
da.Fill(dataTable);
conn.Close();
da.Dispose();
Then you can use dataTable object to see if particular values exist.
I need to serialize a SqlDataReader to Json.
I researched some examples on the internet, however, all the results were a "text" and not an object with lines.
What am I doing wrong?
My implementation:
public JsonResult Teste()
{
using (SqlConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["Conexao"].ConnectionString))
{
db.Open();
using (SqlCommand comando = new SqlCommand("select * from USERS", db))
{
using (SqlDataReader reader = comando.ExecuteReader())
{
DataTable dataTable = new DataTable();
dataTable.Load(reader);
var resultado = JsonConvert.SerializeObject(dataTable);
return Json(resultado, JsonRequestBehavior.AllowGet);
}
}
}
}
My results:
"[{\"UsuarioID\":1,\"Email\":\"admin\",\"Nome\":\"SISTEMA\"},{\"UsuarioID\":2,\"Email\":\"marlon.tiedt#gmail.com\",\"Nome\":\"Marlon Tiedt\"},{\"UsuarioID\":3,\"Email\":\"marlon.tiedt#megasul.com.br\",\"Nome\":\"Marlon - Megasul\"}]"
Desired results:
[{"UsuarioID":1,"Email":"admin","Nome":"SISTEMA"},{"UsuarioID":2,"Email":"marlon.tiedt#gmail.com","Nome":"Marlon Tiedt"},{"UsuarioID":3,"Email":"marlon.tiedt#megasul.com.br","Nome":"Marlon - Megasul"}]
You're double-encoding the output, just pass the dataTable object to new Json(...) and it should work fine. See the modified code below
public JsonResult Teste()
{
using (SqlConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["Conexao"] .ConnectionString))
{
db.Open();
using (SqlCommand comando = new SqlCommand("select * from USERS", db))
{
using (SqlDataReader reader = comando.ExecuteReader())
{
DataTable dataTable = new DataTable();
dataTable.Load(reader);
return Json(dataTable, JsonRequestBehavior.AllowGet);
}
}
}
}
Json internally serializes whatever data you pass to it.In your case you are passing serialized string instead of actual data so it is adding slashes in the response.So remove serialization and pass object directly to Json,you will get proper json response.
-- One Standard way of doing this is convert your datatable into List<Dictionary<string, object>>. Below is the sample code.
public JsonResult Teste()
{
using (SqlConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["Conexao"].ConnectionString))
{
db.Open();
using (SqlCommand comando = new SqlCommand("select * from USERS", db))
{
using (SqlDataReader reader = comando.ExecuteReader())
{
DataTable dataTable = new DataTable();
dataTable.Load(reader);
var resultado = GetTableRows(dataTable);
return Json(resultado, JsonRequestBehavior.AllowGet);
}
}
}
}
public List<Dictionary<string, object>> GetTableRows(DataTable dtData)
{
List<Dictionary<string, object>>
lstRows = new List<Dictionary<string, object>>();
Dictionary<string, object> dictRow = null;
foreach (DataRow dr in dtData.Rows)
{
dictRow = new Dictionary<string, object>();
foreach (DataColumn col in dtData.Columns)
{
dictRow.Add(col.ColumnName, dr[col]);
}
lstRows.Add(dictRow);
}
return lstRows;
}
I am trying to doing this:
Read a row from an SQLite db (in GetRuleByID() method)
Update the same row that I just read during (1) (See UpdateStatusForRuleID() method)
However my problem is that SQLite locks the database after the SELECT in GetRuleByID() so that update in UpdateStatusForRuleID() is only successful when called the first time.
I have tried enabling Write-Ahead-Logging in SQLite as well as PRAGMA read_uncommitted=1 in order to avoid SQLite locking the database for the SELECT, but this does not appear to work.
This should be simple but I have so far spent a complete night trying to solve this... Please help !
private static MicroRuleEngine.Rule GetRuleByID(int ruleID, SQLiteConnection connection, out Dictionary<string, string> dict)
{
dict = new Dictionary<string, string>();
string sql = String.Format("select * from rules WHERE ID = {0} ", ruleID.ToString());
SQLiteCommand command = new SQLiteCommand(sql, connection);
SQLiteDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
// Convert row into a dictionary
for (int lp = 0; lp < reader.FieldCount; lp++)
{
dict.Add(reader.GetName(lp), reader.GetValue(lp) as string);
}
string json = dict["fulljson"];
MicroRuleEngine.Rule r = Newtonsoft.Json.JsonConvert.DeserializeObject<MicroRuleEngine.Rule>(json);
//command.Dispose();
return r;
}
}
internal static void UpdateStatusForRuleID(SQLConnectionManager DBMANAGER, int ruleID, bool status)
{
Dictionary<string, string> dict = null;
string dbVal = (status) ? "1" : "0";
MicroRuleEngine.Rule r = null;
string newJSON = null;
using (SQLiteConnection connection = DBMANAGER.CreateConnection())
{
r = GetRuleByID(ruleID, connection, out dict);
r.Active = (status);
newJSON = Newtonsoft.Json.JsonConvert.SerializeObject(r);
Thread.Sleep(1000);
string sql = "UPDATE rules SET active = #a, fulljson=#j WHERE ID = #i";
using (var command = new SQLiteCommand(sql, connection))
{
command.Parameters.Add(new SQLiteParameter("#a", dbVal));
command.Parameters.Add(new SQLiteParameter("#i", ruleID));
command.Parameters.Add(new SQLiteParameter("#j", newJSON));
command.ExecuteNonQuery(); // Database is locked here ???
}
connection.Close();
}
}
"Database is locked" means that some other connection (in the same or some other process) still has an active transaction.
You don't need multiple connections (unless you are using multiple threads); just use a single connection object for all database accesses.
Ensure that all command, reader, and transaction objects (and connections, if you decide to use temporary ones) are properly cleaned up, by using using:
using (var command = new SQLiteCommand(sql, connection))
using (var reader = command.ExecuteReader())
{
if (reader.HasRows)
...
}
Apparently, the code below works. I basically dropped the GetRuleByID() method (but then I had to re-write 4 other methods)
Thanks to all who provided input.
internal static void UpdateStatusForRuleID(SQLConnectionManager DBMANAGER, int ruleID, bool status)
{
string dbVal = (status) ? "1" : "0";
MicroRuleEngine.Rule r = null;
string newJSON = null;
using (SQLiteConnection conn = DBMANAGER.CreateConnection())
{
string sql = String.Format("select * from rules WHERE ID = {0} ", ruleID.ToString());
using (var command = new SQLiteCommand(sql, conn))
using (var reader = command.ExecuteReader())
{
if (reader.HasRows)
{
reader.Read();
string json = reader["fulljson"].ToString();
r = Newtonsoft.Json.JsonConvert.DeserializeObject<MicroRuleEngine.Rule>(json);
r.Active = (status);
newJSON = Newtonsoft.Json.JsonConvert.SerializeObject(r);
string sql2 = "UPDATE rules SET active = #a, fulljson=#j WHERE ID = #i";
using (var command2 = new SQLiteCommand(sql2, conn))
{
command2.Parameters.Add(new SQLiteParameter("#a", dbVal));
command2.Parameters.Add(new SQLiteParameter("#i", ruleID));
command2.Parameters.Add(new SQLiteParameter("#j", newJSON));
command2.ExecuteNonQuery();
}
}
}
}
}