I've written a simple app (call it app1) that reads a SQLite database and display the contents in a gridview. I have a separate C# console app (app2) that needs to write to the same database. The problem is app2 fails with a "database is locked" error. I can see as soon as I start app1 a userdb-journal file is created. I assume the problem is that app1 opens the database but doesn't release it? This is the code I have for populating the Table I bind to the grid in app1.
public DataTable GetAllPeople()
{
var connectionString = "Data Source=" + dbPath + ";Version=3";
using (SQLiteDataAdapter sqlDataAdapter =
new SQLiteDataAdapter("SELECT id,FirstName,LastName,Address FROM Users",
connectionString))
{
using (DataTable dataTable = new DataTable())
{
sqlDataAdapter.Fill(dataTable);
// code to add some new columns here
return dataTable;
}
}
}
Here is the code that populates the gridview:
private void Form1_Load(object sender, EventArgs e)
{
UserDatabase db = new UserDatabase();
db.Initialize();
dataGridView1.DataSource = db.GetAllPeople();
}
How can I fix things so app2 can read and write to the database while app1 is running?
EDIT
Looks like that journal file is only created by app2. I had only noticed the database locked error when app1 was running also, but perhaps app1 is a red herring. App2 is multi-threaded. Perhaps I should start a new question focusing on app2 and multithreaded access?
EDIT
Thanks for all the comments. I've put a lock around all db accesses and wrapped everything up in usings. All seems to be working now.
Here is the code, set the parameters easily on the connection string builder, and build the SQLiteConnection with it.
SQLiteConnectionStringBuilder connBuilder = new SQLiteConnectionStringBuilder();
connBuilder.DataSource = filePath;
connBuilder.Version = 3;
connBuilder.CacheSize = 4000;
connBuilder.DefaultTimeout = 100;
connBuilder.Password = "mypass";
using(SQLiteConnection conn = new SQLiteConnection(connBuilder.ToString()))
{
//...
}
Regards.
Have you asked SQLITE to wait and try again if the db is locked? Here's how to do it in C
// set SQLite to wait and retry for up to 100ms if database locked
sqlite3_busy_timeout( db, 100 );
The point is that SQLITE locks the db briefly when it is accessed. If another thread or process accesses it while blocked, SQLITE by default returns an error. But you can make it wait and try again automatically with the above call. This solves many of these kind of problems.
Related
I am working on a c# service(built as a console app for now for debugging) that will is polling against a DB tables row count. Upon a new row being added to the table(INSERT) the application will kick off to begin some other processing however i'm having issue with the polling logic.
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
//1 second pause
SqlConnection conn = new SqlConnection(#"OMITTED"); //connection to DB
conn.Open();
SqlDataAdapter adapter = new SqlDataAdapter("OMITTED", conn); //Query to fil poll table comparator
DataTable table = new DataTable(); //establishing new instance to fill
adapter.Fill(table);
int prevRowCount = table.Rows.Count; // establishing comparator value
while (true) //loop
{
adapter.Fill(table);
if (table.Rows.Count > prevRowCount)
{
Console.WriteLine("New records");
prevRowCount = table.Rows.Count;
}
Thread.Sleep(1000); //1 second pause
}
}
}
}
Watching the data table variable in debug and prevrowcount, they both seem so be sitting at 2, however the if is still stepped into. And then upon the next adapter.Fill it seems the value is getting incremented by the row count every iteration. So my question(s):
When using this structure do I need to clear the instance of the variable every loop after the if, if so how?
Why is the if performing upon the first if iteration when the data table variable only has 2 rows and the prevrowcount variable is 2?
I've tried moving the adapter fill, removing it from the while. Different ways of setting, I am not understanding why the if is being performed.
I would suggest you use SQL table dependency. you will not need to write code to poll and do all this.
you can refer to this, I have personally used this in my project.
you will get a notification of inserted/modified/deleted records. you can use it accordingly.
Even Microsoft has provided this, but I have never used it.
I'm trying to make a C# Forms program that views data from a database. It needs to request data on startup, cache it, then update it if the DB is updated. I'm trying to do it using SqlDependency. Here's the code:
private const string connectionString = "Data Source=DESKTOP-VT1F04F\\MSSQLSERVER14;Initial Catalog=test1;Trusted_Connection=True";
private void button1_Click(object sender, EventArgs e)
{
SqlDependency.Stop(connectionString);
SqlDependency.Start(connectionString);
ExecuteWatchingQuery();
}
private void ExecuteWatchingQuery()
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
var command = new SqlCommand("select nbase, name from dbo.filial", connection);
var sqlDependency = new SqlDependency(command);
sqlDependency.OnChange += new OnChangeEventHandler(OnDatabaseChange);
command.ExecuteReader();
}
}
private void OnDatabaseChange(object sender, SqlNotificationEventArgs args)
{
//MessageBox.Show("?");
SqlNotificationInfo info = args.Info;
if (SqlNotificationInfo.Insert.Equals(info)
|| SqlNotificationInfo.Update.Equals(info)
|| SqlNotificationInfo.Delete.Equals(info))
{
MessageBox.Show("!");
//todo
}
ExecuteWatchingQuery();
}
It doesn't do anything. ExecuteWatchingQuery competes fully, but OnDatabaseChange never fires. However, if I replace database test1 with a freshly created database watcher_test with a freshly created table, it works as intended. I have tried the following SQL commands:
alter database test1 set enable_broker
CREATE QUEUE ContactChangeMessages;
CREATE SERVICE ContactChangeNotifications ON QUEUE ContactChangeMessages
Both databases have the same owner (as select name, suser_sname(owner_sid) from sys.databases shows), same permissions, they're on the same server, and I can't see any difference between them, in settings, or anywhere else. The program isn't able to access any table in the first database, but is able to access a copy of a table done with insert into from the first into the second table. The program behaves identically on a different computer that uses the same database restored from a backup.
I have a program which is supposed to open, edit, create and save access databases. For saving I copy an empty database with just the tables (just to avoid going through the hassle of creating every table and column etc) and try to fill it with values via the TableAdapterManager.UpdateAll method.
string _TemplateConnectString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};";
_connection = new OleDbConnection(string.Format(_TemplateConnectString, dlg.FileName));
_connection.Open();
DataSet1TableAdapters.TableAdapterManager tam=new TableAdapterManager();
tam.Connection = _connection;
try
{
tam.UpdateAll(dataset);
}
catch (System.Exception ex)
{
MessageBox.Show("Update failed");
}
It finishes with no exceptions but the values don't get inserted into the new database.
Also as far as I know the UpdateAll method only updates modified row so if I open some db and it inserts it's rows, it will not take them into account even though there are not in the database that I am trying to fill.
I have also tried filling the database with the ADODB and ADOX extensions but all the solutions I found with those was a lot of hardcoding and no regards for hierarchy, keys, etc.
Is there a way to force insert everything in the new database?
Is your template database in the Visual Studio project directory? It might have something to do with Visual Studio copying the database to the bin/debug or bin/release folder...
Try to use the right Data source database name, here an
example with an excel file:
cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=c:\somepath\ExcelFile.xls;" & _
"Extended Properties=""Excel 8.0;HDR=Yes;"";"
A clumsy solution but it works. I iterate the tables of the dataset and save the via an sql string generator like this:
void SaveTable(DataTable dt)
{
string[] inserts;
try
{
inserts = SqlHelper.GenerateInserts(dt, null, null, null);
foreach (string s in inserts)
{
OleDbCommand cmd = new OleDbCommand();
cmd.CommandText = s;
cmd.Connection = _connection;
int n = cmd.ExecuteNonQuery();
}
}
catch (Exception e)
{
SaveOk = false;
}
}
I found the SqlHelper somewhere on this site, but completely lost where, unforunately. So here is the pastebin with it https://pastebin.com/iCMVuYyu
I got some data inputed by the user that should be added to a Database File (.sdf). I've choose Sql Server CE because this application is quite small, and i didn't saw need to work with a service based database.
Well any way.
Here goes the code:
public class SqlActions
{
string conStr = String.Format("Data Source = " + new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName + "\\basedados.sdf");
public SqlCeConnection SQLCEConnect()
{
SqlCeConnection Connection = new SqlCeConnection(conStr);
Connection.Open();
return Connection;
}
public Boolean AdicionarAuditorio(string Nome, int Capacidade)
{
string Query = "INSERT INTO auditorios (nome, capacidade) VALUES (#Nome, #Capacidade)";
using (var SQLCmd = new SqlCeCommand(Query, SQLCEConnect()))
{
SQLCmd.Parameters.AddWithValue("#Nome", Nome);
SQLCmd.Parameters.AddWithValue("#Capacidade", Capacidade);
if (SQLCmd.ExecuteNonQuery() == 1)
{
return true;
} else {
return false;
}
}
}
}
I use the AdicionarAuditorio(string Nome, int Capacidade) function to Insert the data. running ExecuteNonQuery() which is supposed to return the number of affected rows after he as run the query.
So it should return 1 if the query as successful, right?
In the end he returns 1, but if I browser the table data, the data that the query should add isn't there.
So whats wrong here?
NOTE. If your thinking that the
problem is the connection: I can't see
why is the problem once i got some
Select statements that use that
connection function SQLCEConnect()
and they all work pretty well.
Thanks in advance.
Are you sure you are looking at the right file? When you build your app in VS, it copies the SDF file as content to the target folder, so the database in your project will not reflect any updates. Your code is picking up the the file location there.
This is btw not a good practice, because once deployed, the program folders are not writable to your app (could this be the problem - did you already deploy?). Instead, the database file should reside in your appdata folder.
Is it possible that you make the call to AdicionarAuditorio in a TransactionScope without calling transactionScope.Complete()?
I'm using Access 2007 and C# to learn Databases. So far it's been rough but I've been able to handle things relatively well. What I need to do though is to query a my database table Accounts for the Amount of money a user has based on their pin. I've placed a button on the Windows Form I am using that will query the database on click. When I run/click the button as per normal I recieve the following error.
Essentially my question is this: How would I go about setting the permissions up so that my program can freely access the Access Database I have?
My Exception Error:
Exception: System.Data.OleDb.OleDbException: The Microsoft Office Access database engine cannot open or write to the file 'C:\Users\Public'. It is already opened exclusively by another user, or you need permission to view and write its data.
My code:
public partial class frmPin : Form
{
static string connString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Public;Persist Security Info=True";
static private int pin = 11; //The First Pin in my records, for debugging I inserted it directly.
static string selectStatement = "SELECT Amount FROM Accounts WHERE(PIN=" + pin + ")";
OleDbConnection conn = new OleDbConnection(connString);
OleDbCommand cmd = new OleDbCommand(selectStatement);
public frmPin()
{
InitializeComponent();
}
private void btnQry_Click(object sender, EventArgs e)
{
try
{
conn.Open();
OleDbDataReader reader = cmd.ExecuteReader(); // executes query
while (reader.Read()) // if can read row from database
{
txtBx.Text = reader.GetValue(1).ToString();
}
}
catch (Exception ex)
{
txtBx.Text = "Exception: " + ex; // Displays Exception
}
finally
{
conn.Close(); // finally closes connection
}
}
"C:\Users\Public" needs to be changed to the actual path of the *.mdb file you want to access:
"C:\Users\Public.mdb"
OR
"C:\Users\Public\Something.mdb"
Depending on the name of your database:
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\mydatabase.mdb;User Id=admin;Password=;
Or it may be an *.accdb file. Such as:
Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\myFolder\myAccess2007file.accdb;Persist Security Info=False;
See http://www.connectionstrings.com/access-2007 and http://www.connectionstrings.com/access
Also, sometimes you will get this kind of problem if you have the file open in another program like Access 2007, the file is marked as Read Only, or the security permissions are such that you don't have Read or Write Access. Note that if you set a "Deny" permission (in the filesystem/NTFS) for a group like Users, then it will override all other permissions, such that an Administrator would be effected by the Deny permission.
Edit: Thanks for comments, added a little clarification.