SQLite-net throwing exception on second call to ExecuteNonQuery - c#

I am using SQLite-net for accessing an SQLite database file in a WinRT app. I don't have any problems reading from the database using the ExecuteQuery (I actually use the modified version from https://github.com/praeclarum/sqlite-net/issues/82 as I don't use table mappings and want the results as dictionary which calls ExecuteDeferredQuery underneath).
When I try to insert records into my database using ExecuteNonQuery, I am getting an exception with the message "CannotOpen".
Well, just a few lines above, I can read from the database successfully. I double checked the file permissions to the sqlite database file and gave everyone on the computer full control to the file to avoid any file permission issues, but the result is the same, still getting "CannotOpen".
I just tried to do a select statement with ExecuteNonQuery (just to see if it works"), I still get an exception, this time saying "Row" as the exception message.
I tried to execute my insert with ExecuteQuery to see what happens, no exception is thrown, everything seems OK, but no rows are inserted into my database.
Well, that may be explainable as ExecuteQuery is designed for querying, not inserting, but what could be the reason for ExecuteNonQuery throwing exceptions?
Here is my code (removed actual table names and parameters with generic ones for privacy):
SQLiteCommand cmd = conn.CreateCommand("insert into mytable(myfields...) values (?,?,?,?,?,?,?)", my parameters...);
cmd.ExecuteNonQuery(); //throws SQLiteException
However this code doesn't throw exception:
SQLiteCommand cmd = conn.CreateCommand("select * from mytable where some condition...", some parameters...);
var result = cmd.ExecuteToDictionary(); //renamed ExecuteQuery method from https://github.com/praeclarum/sqlite-net/issues/82
UPDATE: I've further tracked the issue down to something even simpler (and weird). This code is the very first call to SQLite framework after initialization of the connection. This very code, when executed, throws an exception in the fourth line:
SQLiteCommand cmd = conn.CreateCommand("select * from mytable");
cmd.ExecuteNonQuery();
cmd = conn.CreateCommand("select * from mytable"); //yes, the same simple query as above
cmd.ExecuteNonQuery();//getting error
UPDATE 2: If I call ExecuteToDictionary instead of ExecuteNonQuery, it works.
UPDATE 3: If I try a direct query (from the conn object such as conn.Execute("query...")) before all these calls it fails. If it's an insert query, I get CannotOpen error, if it's a select query, I get a Row error.
Why am I getting an exception on the second call to ExecuteNonQuery?
Why am I getting a different error message "Row" when I try SELECT with ExecuteNonQuery? And lastly, why are these exceptions so user-unfriendly?

Found out the answer. The SQLite file was in a directory that didn't have write access (the file DID have all the access in file properties, but I think it's a WinRT security model issue as the file was outside the sandbox of WinRT's storage folders. I could read the file, but not write. Thanks to SQLite-net's extremely helpful exception messages such as "Row" and "CannotOpen", without giving any real details about the problem, it took me days to realize that it was a file access issue rather than an SQLite configuration/query issue.
If anyone has any similar problems in the future, first, check that the SQLite database is in the storage directory of the WinRT app.

try to close and open the connection object before executing any other operations

Related

Auto truncate fields with ODBCDataAdapter on INSERT and UPDATE

I am updating a database using System.Data.Odbc.OdbcDataAdapter. I also don't know what table or fields I'm updating until runtime. The code I have works and is roughly equivalent to the following:
{
mConn = new OdbcConnection(connectionStr);
mConn.Open();
mDataSet = new DataSet();
mDataAdapter = new OdbcDataAdapter(selectStatement, mConn);
mCmdBldr = new OdbcCommandBuilder(mDataAdapter);
var trans = mConn.BeginTransaction();
mDataAdapter.SelectCommand.Transaction = trans;
mDataAdapter.Fill(mDataSet, tableName);
mDataTable = mDataSet.Tables[tableName];
mDataTable.Rows.Add(mNewDataTableRow);
mDataAdapter.Update(mDataSet, tableName);
trans.Commit();
}
The above is stripped down from my actual code, but hopefully gives an idea of how I'm using the DataAdapter. As I say, it works, for the most part...
It does of course fail if I try to insert to many characters into a SQL Server VARCHAR column, in which case I get the following error (in this case, I'm writing to SQL Server, but the database could be something else):
System.Data.Odbc.OdbcException occurred HResult=0x80131937
Message=ERROR [22001] [Microsoft][ODBC SQL Server Driver][SQL
Server]String or binary data would be truncated.
My question is this:
Is there any way in which I can get the OdbcDataAdapter to automatically truncate text fields?
I understand I can turn off ANSI_WARNINGS in the database if I'm using SQL Server, but I might not be, and I don't want to do that anyway because in most cases I would want the above exception to be thrown. I also understand that I can inspect the constraints on the fields myself and truncate the data before I insert it, but I'm looking for something less manual. I'll write that code if I have to, but I'd rather not have to.
I've looked at setting the OdbcParameter.Size property, but this simply throws an exception in a different place. Similarly with DataColumn.MaxLength.
I've also looked at setting DataSet.EnforceConstraints, but that doesn't prevent the above error when actually updating.

IndexOutOfRangeException when calling SqlQuery.ExecuteAsCollection<T>() but not in the Command & Watch Windows

I have a very strage problem with the SqlQuery.ExecuteAsCollection<T>() method and I am not sure whether this is related to SubSonic, my database or .NET.
When the following line gets executed:
FlowerCollection myCollection = sqlQuery.ExecuteAsCollection<FlowerCollection>();
my application crashes with the IndexOutOfRangeException that occured in System.Data.dll and the Additional Information containing the name of a column in the Flower table (which does exist both in the database itself and in the generated SubSonic class). However when I execute the same line in the Command Widnow or in the Watch window I get the result I expect without any errors.
I tried to load the debug symbols for .NET but in this case there seem to be no source code available so I am not able to debug it like that.
Have you got any ideas what else I can try to find the bug?
EDIT: I just added a try/catch around this code block and it throws exeptions for each column in the Flower table.
I found a workaround...
my original SqlQuery was constructed with an argument limiting the columns in the result:
SqlQuery sqlQuery = new Select(new String[] { Flower.Columns.Name }).From(Tables.Flower)
after I removed the argument from the Select constructor it runs without throwing any exception:
SqlQuery sqlQuery = new Select().From(Tables.Flower)
Why it worked in the Command & Watch in spite of this remains a mistery...

c# adapter.fill dataset not working with stored procedure even though I know the parameters are returning data?

I have the strangest issue. The following works most of the time, however "now and again" it fails to return data, even though I have put a breakpoint for the adapter.fill statement, checked the params and executed them via SQL Management Studio which definitely returns the data :)
So this is a standard c# SQL client call:
SqlCommand command = new SqlCommand();
command.CommandType = CommandType.StoredProcedure;
command.Connection = connection;
The connection is definitely being opened ok, and used because it is also being used via another call.. and actually subsequent calls work too.. however, again in my case the following "sometimes" fails:
command.CommandText = "myprocname";
command.Parameters.AddWithValue("#param1", param1value);
command.Parameters.AddWithValue("#param2", param2value);
command.Parameters.AddWithValue("#param3", param3value);
command.Parameters.AddWithValue("#param4", param4value);
and then to fill the dataset:
DataSet ds = new DataSet();
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(ds);
}
command.Dispose();
So really nothing strange going on, and the above works most of the time.. on the occasion it didn't return what I expected, and put a breakpoint in just before the adapter.fill statement, checked the commandtext and each of the parameters/values being passed.. opened up sql management studio, ran the proc with those params, and sure enough the expected data was returned.
The proc is very simple, and again most of the time returned the data...
I cannot fault the proc in anyway, however this is driving my crazy as I have checked connection timeouts, timings of execution and so on.. I guess the craziest thing is that the proc name, and params are all correct (no extra params being passed by accident etc).. and running manually, it returns the data.. but via the fill statement it seems to NOT (but only occasionally!).
Any help with this madness much appreciated!!
RESOLVED: Okay well.. the strangest error, one that has really thrown me.
I didn't mention at the time of posting this question, that I was using RDLC/Reportviewer control to show the data within a set of dashboard charts.. i.e. you could then select a chart segment (a pie chart slice for example) to drilldown on that data.. the chart data was "pre-grouped" i.e. the procs for those charts didn't contain the detail to make it quick to show the dashboard..
Anyway, I digress, because I had some "bad data".. well data that had null dates, and the short story version was the grouping in the reportview chart was somehow causing the selected data to be incorrect "sometimes".. as in the params passed were/looked ok, but on occasion they were not..
So the end result is (which I had planned anyway) was to add checks into my procs for "date is not null" etc.. however it didn't harm to wrap the command object etc with "using" statements too! so many thanks for all the responses!

Why might IDbCommand.ExecuteReader() fail when IDbCommand.ExecuteScalar() succeeds?

I have some C# code that dynamically generates an SQL query and executes it via IDbCommand.ExecuteScalar(). This works fine; there's exactly one result that matches my query in the DB, and that result is always returned.
But just recently, as the first step in a refactoring to support multiple matches in the DB, I replaced the call to ExecuteScalar() with one to ExecuteReader(). Everything else in the setup and DB access is the same. But the returned IDataReader contains no data, and throws InvalidOperationExceptions whenever I try to get data out of it.
I know the data's still there; everything works fine when I switch back to ExecuteScalar(). How is this possible?
Make sure that you are calling the Read() method on the IDataReader that is returned by ExecuteReader() before trying to access it. Calling Read() will advance the reader onto the first (and in your case only) row of the resultset. If you do not call Read() before accessing the IDataReader, you will get an InvalidOperationException when you try to access its data - as you are experiencing.
Is this to do with having multiple IDataReaders open on the same connection?
Because you wont get that issue with ExecuteScalar(), but once you start using ExecuteReader() you need to make sure all previous DataReaders on the same connection are closed (e.g. by using a 'using' block)
What is the error message that you get with the InvalidOperationException?

Why is OdbcCommand.ExecuteScalar() throwing an AccessViolationException?

I have a block of code intended to pull text descriptions from a database table and save them to a text file. It looks like this (C# .NET):
OdbcCommand getItemsCommand = new OdbcCommand("SELECT ID FROM ITEMS", databaseConnection);
OdbcDataReader getItemsReader = getItemsCommand.ExecuteReader();
OdbcCommand getDescriptionCommand = new OdbcCommand("SELECT ITEMDESCRIPTION FROM ITEMS WHERE ID = ?", databaseConnection);
getDescriptionCommand.Prepare();
while (getItemsReader.Read())
{
long id = getItemsReader.GetInt64(0);
String outputPath = "c:\\text\\" + id + ".txt";
if (!File.Exists(outputPath))
{
getDescriptionCommand.Parameters.Clear();
getDescriptionCommand.Parameters.AddWithValue("id", id);
String description = (String)getDescriptionCommand.ExecuteScalar();
StreamWriter outputWriter = new StreamWriter(outputPath);
outputWriter.Write(description);
outputWriter.Close();
}
}
getItemsReader.Close();
This code has successfully saved a portion of the data to .txt files, but for many rows, an AccessViolationException is thrown on the following line:
String description = (String)getDescriptionCommand.ExecuteScalar();
The Exception text is "Attempted to read or write protected memory. This is often an indication that other memory is corrupt".
The program will usually throw the exception on the same rows of the table, but it doesn't appear to be 100% consistent. Sometimes data that had thrown the exception in the past will suddenly work.
Some people are undoubtedly wondering why I didn't just SELECT ID, ITEMDESCRIPTION FROM ITEMS in the getItemsCommand and skip the second query. Actually, I did it that way initially, and I was encountering the same error with getItemsCommand.GetString(). I was afraid that perhaps the dataset was taking up too much memory and maybe that was causing the error. So I decided to try this method to see if it would help. It didn't. Does anyone know why this might be happening?
By the way, ID is an INT and ITEMDESCRIPTION is a VARCHAR(32000) column. If it makes any difference, the database is Borland Interbase 6.0 (Ick!)
EDIT: I gave the wrong line when describing where the exception was being thrown!! ARGH!! Fixed now. Also, I've tried the things suggested so far, but they didn't help. However, I found that only very old records in the database were causing this error, which is strange. If I change the query to only pull records inserted in the last 5 years, there are no problems. Someone suggested to me this might be an encoding conversion problem or something like that?
Update: Solved it. The problem turned out to be a bug in the ODBC driver for our not-very-reliable database software. A workaround with other drivers fixed the problem.
It could be a bug in the ODBC driver you are using. What driver is it? What is your connection string?
A shot in the dark here...
Try executing your reader, saving your result (maybe in an array or list), and making sure the reader is closed before executing or preparing the next command. You may even want to go extreme and put your getItemsCommand construction inside a using block so you know that it has no resources open before you execute your next command...

Categories