I'm writing an application using C# and System.Data.SQLite (the library from https://system.data.sqlite.org) and I feel like I'm beating my head against a wall. My unit tests on my larger chunks of code are randomly throwing exceptions, so in preparation for asking a question here, I started a new project with as small a chunk of code as I can. This chunk of code, which creates a new sqlite file and then adds a table to it, keeps throwing a Database is not open error whenever it attempts to add the table.
Looking at the similar questions on Stack Overflow, many of them are regarding using SQLite with Android, which is not the case here.
In my research, I've seen that I want to keep the connection open only for as long as I need it, but as you can see from the code sample, I'm using using to isolate the connection and command right next to each other, but I'm still having problems.
There's probably something obvious I'm doing wrong, but I'm at a loss to figure out what it is.
Thanks!
using System.Data.SQLite;
namespace SmallCode {
class Program {
private const string DB_NAME = "Test.sqlite";
private const string DB_CONN_STRING = "Data Source=" + DB_NAME + ";Version=3;";
static void Main(string[] args) {
Program p = new Program();
SQLiteConnection.CreateFile(DB_NAME);
using (SQLiteConnection c = new SQLiteConnection(DB_CONN_STRING)) {
string sqlCreateTableAccount = "CREATE TABLE ACCOUNT (ID INTEGER PRIMARY KEY, NAME TEXT NOT NULL);";
using (SQLiteCommand cmd = new SQLiteCommand(sqlCreateTableAccount, c)) {
// EXCEPTION THROWN ON NEXT LINE
cmd.ExecuteNonQuery();
}
}
}
}
}
You still need to .Open the connection...
using (SQLiteConnection c = new SQLiteConnection(DB_CONN_STRING)) {
c.Open(); //<------ ADD THIS LINE
string sqlCreateTableAccount = "CREATE TABLE ACCOUNT (ID INTEGER PRIMARY KEY, NAME TEXT NOT NULL);";
using (SQLiteCommand cmd = new SQLiteCommand(sqlCreateTableAccount, c)) {
// EXCEPTION THROWN ON NEXT LINE
cmd.ExecuteNonQuery();
}
}
Creating the connection with using doesn't automatically open the connection, it just creates it. You don't need to explicitly .Close() it at the end though, it will be closed when the using disposes it.
Related
I'm working on a small project that's unrelated to this, it needs a database to be integrated into it, so I looked into SQLite and tried to teach myself the basics.
My code works fine but I realised if I was to deploy this on another machine it wouldn't be able to connect to the database because it wouldn't have the file as I've hard coded it in (it's running of my C:\ drive currently which will be an issue for users who haven't got it there obviously).
I was wondering if it was possible to update to connection string for the user at runtime? Even say, when the user installs it, it installs a copy of the database and changes its path to that?
Here's my code:
using System;
using System.Windows.Forms;
using System.Data.SQLite;
namespace sqltest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string cs = #"Data Source=c:\student.db;Version=3;FailIfMissing=False";
string stm = "SELECT Name FROM Customer";
using var con = new SQLiteConnection(cs);
con.Open();
using var cmd = new SQLiteCommand(stm, con);
using SQLiteDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
MessageBox.Show($"{rdr.GetString(0)}");
}
}
}
}
Thanks for any help!
you can get datasource from config file, e.g. appsetting.json
The connection string that you pass to new SQLiteConnection(...); is actually being passed at runtime already.
Sounds like the simplest solution is to create the database if it doesn't already exists with a predetermined path. This will ensure that when your script runs on a machine that doesn't have a DB, the DB will be created at runtime.
Here's a relevant post: Programmatically create sqlite db if it doesn't exist?
I am creating a virtual table with sqlite fts5 and I am having the following error message: SQL Logic error no such module: FTS5. Below is my code:
Using Package manager in VS 2017 I have already download SQLite and SQLite FTS5.
private static void CreateReport()
{
try
{
using (SQLiteConnection sqliteConnection = new SQLiteConnection(DataSources.LocalConnectionString()))
{
sqliteConnection.Open();
sqliteConnection.EnableExtensions(true);
string commandText = "CREATE TABLE IF NOT EXISTS JReport(JRId INTEGER PRIMARY KEY, IDId INTEGER, CaseId INTEGER, BoxName TEXT, JRText TEXT, JRFormatted TEXT)";
string commandText1 = "CREATE VIRTUAL TABLE IF NOT EXISTS DReport USING FTS5(JRId, CaseId, BoxName, CONTENT = 'JReport', CONTENT_ROWID = 'JRId')";
string commandText2 = "CREATE TRIGGER DocRepo AFTER INSERT ON JReport BEGIN INSERT INTO DReport(RowId, JRId, CaseId, BoxName) VALUES(NEW.JRId, NEW.CaseId, NEW.BoxName) END";
using (SQLiteCommand sqliteCommand = new SQLiteCommand(commandText, sqliteConnection))
{
sqliteCommand.ExecuteNonQuery();
sqliteCommand.Dispose();
}
using (SQLiteCommand sqliteCommand = new SQLiteCommand(commandText1, sqliteConnection))
{
sqliteCommand.ExecuteNonQuery();
sqliteCommand.Dispose();
}
using (SQLiteCommand sqliteCommand = new SQLiteCommand(commandText2, sqliteConnection))
{
sqliteCommand.ExecuteNonQuery();
sqliteCommand.Dispose();
}
sqliteConnection.Close();
}
}
catch (Exception ex)
{
MessageBoxEx.Show("An error has occurred while creating the Report table, the original error is: " +
ex.Message, "Report", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
SQL Logic error no such module: FTS5.
Just like the error message says, your sqlite3 library doesn't have the FTS5 module. It probably wasn't configured to include it as a built in one when the library was built, as it's not enabled by default. It might have been made available as a dynamically loadable module by whoever did configure and build the library you're using. Or not.
Personally, I just always include a copy of sqlite3.c in any program I'm using that uses it to avoid relying on an external dependency and so you can make sure you're always using a version with all the features you want to use present. Dunno what you'd have to do in C# to use your own local instance, but I'm sure there's a way.
Instructions for building FTS5 into sqlite3 or as a loadable module.
I would like some help with my Visual Studio C# code with inserting unicode strings into a SQLite database.
Below is my test code to write a test string to the database:
string testStr = "á Á ñ ç é á";
SQLiteConnection mydataConnection = new SQLiteConnection(); // setup new sql connection obj
try
{
//// SQLite DB
mydataConnection.ConnectionString =
"Data Source=C:\\Users\\John\\Desktop\\location.db; Version=3; UseUTF16Encoding=True;Synchronous=Normal;New=False"; // set up the connection string
mydataConnection.Open(); // open the connection to the db
SQLiteCommand myCmd = new SQLiteCommand(); // create a new command object
myCmd.Connection = mydataConnection; // whats its connected to, see above connection string
SQLiteParameterCollection myParameters = myCmd.Parameters; // good pratice to use parameters to pass data to db
myParameters.AddWithValue("#name", testStr); //
myCmd.CommandText = "INSERT INTO location (name) VALUES (#name)";
myCmd.ExecuteNonQuery();
}
catch (SQLiteException d)
{
string myerror = "Database Error" + d;
MessageBox.Show(myerror);
}
finally // good pratice to close db connection in a finally so exceptions dont leave open.
{
mydataConnection.Close();
}
When I view the database/table (using SQLite Administrator) the string looks like this:
á à ñ ç é á
As a test, I can copy & paste the string direct into the database using SQLite Administrator and the string is saved and can subsequently be viewed OK.
I have tried toggling "UseUTF16Encoding=True;" True / False - no difference.
Any ideas what I'm doing wrong.
This problem turned out to be an issue with the SQLite Administrator app I was using to view/check the db/data. Seems it was this app that would not display the characters correctly when inserted by my code. Strangely though if you used the SQLite Administrator app to add the test text directly (by copy & pasting the test string into the table/field) it would display, save & subsequently view OK. Anyway now using SourceForge SQLite Database Browser to check my code writes correctly and all seems in order.
Many thanks for anyone who took the time to comment, hope this is of help to someone else.
You can try with this code
byte[] encod = Encoding.Default.GetBytes(testStr );
string result = Encoding.UTF8.GetString(encod);
myParameters.AddWithValue("#name", result);
there is no error just a clarification
I am trying to insert into a table called "Test" which has one column, a primary key called "id". I am new to using databases in visual studio and think there is something wrong with my insert syntax since all other functions work fine. is the general layout like this?
using (SqlCommand command = new SqlCommand("INSERT INTO Test (id) VALUES(1)", conn))
overall code looks like this:
class Program
{
static void Main(string[] args)
{
string connection =
"Data Source=.\\SQLEXPRESS;" +
"User Instance=true;" +
"Integrated Security=true;" +
"AttachDbFilename=|DataDirectory|HaythamService.mdf;";
try
{
using (SqlConnection conn = new SqlConnection(connection))
{
conn.Open();
using (SqlCommand command = new SqlCommand("INSERT INTO Test (id) VALUES(1)", conn))
{
command.ExecuteNonQuery();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
Console.WriteLine(reader.GetValue(i));
}
Console.WriteLine();
Console.Read();
}
}
}
conn.Close();
}
}
catch (Exception ex)
{
}
}
}
First of all: swallowing the exception will not help. Remove the try-catch-block and let your application run into the error - then you'll see what the problem is.
Secondly: You're executing the INSERT command using ExecuteNonQuery and right after that try to read from it using an SqlDataReader? What kind of output do you expect from that?
Probably, if ID is a primary key column, that will also be the cause of a primary key violation error, because actually you're executing the same insert twice using the same value - and this is not allowed for a primary key.
To put it short: Create a second command you ExecuteReader. The command should read SELECT * FROM Test.
Thirdly: Do use the SqlConnectionStringBuilder class to create your connection string instead of hardcoding it. This will help you against invalid connection strings.
As I can read from SQLite FAQ it supports multiple processes reading (SELECT) and only one process writing (INSERT, UPDATE, DELETE) database at any moment in time:
SQLite uses reader/writer locks to control access to the database.
When any process wants to write, it must lock the entire database file
for the duration of its update. But that normally only takes a few
milliseconds. Other processes just wait on the writer to finish then
continue about their business
I'm using System.Data.SQLite adapter via c#.
Could someone expalin me plz, how exactly this process is going on?
Will this process work automatically and writing SQLiteCommand will simply wait if there is another writing SQLiteCommand already executing over the same database?
Or maybe it will throw an exception? What kind of it?
Sorry but I found no information about this mechanics :)
Thank you.
UPDATE:
I've found post saying that exception will be raised with a specific errorcode
Is that statement correct?
I've investigated it by myself:
I created a sample SQLite database c:\123.db with one table Categories containing two fields: ID (uniqueidentifier) and Name (nvarchar).
I then wrote some multi-thread code to emulate multiple write access to the database (don't forget to add a System.Data.SQLite reference to your project if you use this code):
using System;
using System.Data.SQLite;
using System.Threading.Tasks;
namespace SQLiteTest
{
class Program
{
static void Main(string[] args)
{
var tasks = new Task[100];
for (int i = 0; i < 100; i++)
{
tasks[i] = new Task(new Program().WriteToDB);
tasks[i].Start();
}
foreach (var task in tasks)
task.Wait();
}
public void WriteToDB()
{
try
{
using (SQLiteConnection myconnection = new SQLiteConnection(#"Data Source=c:\123.db"))
{
myconnection.Open();
using (SQLiteTransaction mytransaction = myconnection.BeginTransaction())
{
using (SQLiteCommand mycommand = new SQLiteCommand(myconnection))
{
Guid id = Guid.NewGuid();
mycommand.CommandText = "INSERT INTO Categories(ID, Name) VALUES ('" + id.ToString() + "', '111')";
mycommand.ExecuteNonQuery();
mycommand.CommandText = "UPDATE Categories SET Name='222' WHERE ID='" + id.ToString() + "'";
mycommand.ExecuteNonQuery();
mycommand.CommandText = "DELETE FROM Categories WHERE ID='" + id.ToString() + "'";
mycommand.ExecuteNonQuery();
}
mytransaction.Commit();
}
}
}
catch (SQLiteException ex)
{
if (ex.ReturnCode == SQLiteErrorCode.Busy)
Console.WriteLine("Database is locked by another process!");
}
}
}
}
The result on my Core2Duo E7500 is that Exception is never raised!
Looks like SQLite is optimised enough for my needs (locking/unlocking is really fast and normally only takes a few milliseconds as SQLite FAQ tells us) - Great!
Note that there is no need to retrieve an integer ErrorCode for an SQLiteException - you can use a special enum ReturnCode field instead. All codes are described here.
Hope this information will help somebody.