SQLite:数据库被锁定 C#
SQLite: Database Is locked C#
编辑!这是错误的视频 https://www.youtube.com/watch?v=_mJ3o3ykQrw
在我的数据库处理程序中创建了一个名为 AddAttendance 的方法 class。我可以从其他 class 访问它,因为它是静态的。这是代码:
public static void AddAttendance(string ID, string DateTime, string Action, string Session)
{
SQLiteConnection sqlite_conn = new SQLiteConnection("Data Source = " + database + "; Version = 3; New = True; Compress = True;");
try
{
sqlite_conn.Open();
SQLiteCommand sqlite_cmd = sqlite_conn.CreateCommand();
sqlite_cmd.CommandText = "INSERT INTO AttendanceLog (UserID, DateTime, Action, Session) VALUES (" +
"'" + ID + "', '" + DateTime + "', '" + Action + "', '" + Session + "');";
sqlite_cmd.ExecuteNonQuery();
sqlite_conn.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
sqlite_conn.Close();
}
此代码只能由我绑定到按钮的此对话框表单访问:
private void buttonAccept_Click(object sender, EventArgs e)
{
if (textBoxID.Text.Length == 0 || textBoxTimeDate.Text.Length == 0)
{
MessageBox.Show("ID or Time and Date cannot be empty!", "Missing field", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (buttonAccept.Text.Equals("Accept"))
{
buttonAccept.Text = "Confirm!";
return;
}
DatabaseHandles.AddAttendance(textBoxID.Text, textBoxTimeDate.Text, comboBoxAction.Text, comboBoxSession.Text);
MessageBox.Show("Record added!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
DialogResult = DialogResult.OK;
}
如果我从我的 Form Kiosk 模式访问它,它工作得很好,但如果我从其他任何地方访问它,它会报告数据库已锁定。
奇怪的是,当我事先先进入我的 Kiosk 模式然后再访问其他地方时,它工作正常。
我不知道我错过了什么。这是我调用表单时使用的相同代码:
AttendanceInsert ai = new AttendanceInsert();
ai.ShowDialog();
不知道能不能解决你的问题,但没理由不做对:
public static void AddAttendance(string id, string datetime, string action, string session)
{
try
{
using (var sqlite_conn = new SQLiteConnection("Data Source = " + database + "; Version = 3; New = True; Compress = True;")
{
sqlite_conn.Open();
using (var sqlite_cmd = sqlite_conn.CreateCommand())
{
sqlite_cmd.CommandText = "INSERT INTO AttendanceLog (UserID, DateTime, Action, Session) VALUES (@Id, @DateTime, @Action, @Session);";
sqlite_cmd.Parameters.Add(new SqlParameter("@Id", SqlDbType.NVarChar)).Value = id;
sqlite_cmd.Parameters.Add(new SqlParameter("DateTime", SqlDbType.NVarChar)).Value = datetime;
sqlite_cmd.Parameters.Add(new SqlParameter("Action", SqlDbType.NVarChar)).Value = action;
sqlite_cmd.Parameters.Add(new SqlParameter("Session", SqlDbType.NVarChar)).Value = session;
sqlite_cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
这样您的连接和 SqlCommand 将被关闭和处理,并且您正在使用参数化查询,这将防止 SQL 注入。如果您的数据库列不是 NVarChar,则需要更改它。您还可以在参数定义中添加长度。
此锁定问题是由于尝试 re-use 数据库连接而引起的。 ADO.Net 有一个称为连接池的功能,这样 re-using 在整个应用程序中使用相同的连接对象是一个 非常糟糕的主意 这实际上会使事情变得 更慢,除了导致像这样的锁定问题。
相反,您确实应该为大多数查询创建一个全新的连接对象,在 运行 查询之前立即打开连接,并尽快 处理 对象当查询完成时。最简单的方法是通过 using
块:
private static string ConnectionString {get;} = "Data Source = " + database + "; Version = 3; New = True; Compress = True;"
public static void AddAttendance(string ID, string dateTime, string Action, string Session)
{
string sql = "INSERT INTO AttendanceLog (UserID, DateTime, Action, Session) VALUES (@id, @time, @action, @session);";
using (var conn = new SQLiteConnection(ConnectionString))
using (var cmd = new SQLiteCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@id", ID);
// Note the DateTime format. It's necessary to store date values in this way in order to preserve cardinality and allow indexes and date math to function properly.
// Better DB platforms would have a DateTime type to use instead
cmd.Parameters.AddWithValue("@time", DateTime.Parse(dateTime).ToString("yyyy-MM-dd HH:mm:ss"));
cmd.Parameters.AddWithValue("@action", Action);
cmd.Parameters.AddWithValue("@session", session);
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
using
块将保证 正确关闭连接,即使在 sqllite_conn.Close()
调用可能被错过的情况下也是如此。
事情是这样的:修复这个查询是不够的!
您必须修复 ALL 的查询以依赖 using
块, 否则您仍然有风险用于锁定。锁定问题不会显示在实际导致锁定的查询上,而是在 运行 数据库被锁定后 的查询上显示。
在这种情况下,看起来锁定问题可能发生在您显示网格的位置。网格查询 运行 很有趣,没有显示任何错误,但也没有释放它的锁,因此您在尝试添加记录时遇到错误,因为这是之后的下一个查询。
参数也是如此。 仅此查询使用查询参数而不是字符串连接是不够的。你必须这样做每次你将数据替换为SQL字符串,在EVERY查询.
罪魁祸首是 reader,虽然我用 using
参数关闭了所有连接,但我忘记关闭我使用的所有 readers,我应该利用 using
这里也是,但你明白了。数据库已锁定 readers 未关闭
public static void LoadScannedUser(string ID)
{
string sql = "SELECT * FROM Users WHERE ID = '" + ID + "'";
using (var conn = new SQLiteConnection(ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
try
{
cmd.CommandText = sql;
conn.Open();
SQLiteDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
scannedUser.ID = reader.GetString(1);
scannedUser.Password = reader.GetString(2);
scannedUser.PPicture = Cryptorizer.Base64ToImage(reader.GetString(3));
scannedUser.FName = reader.GetString(4);
scannedUser.MName = reader.GetString(5);
scannedUser.LName = reader.GetString(6);
scannedUser.Agency = reader.GetString(7);
scannedUser.Position = reader.GetString(8);
}
reader.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
编辑!这是错误的视频 https://www.youtube.com/watch?v=_mJ3o3ykQrw
在我的数据库处理程序中创建了一个名为 AddAttendance 的方法 class。我可以从其他 class 访问它,因为它是静态的。这是代码:
public static void AddAttendance(string ID, string DateTime, string Action, string Session)
{
SQLiteConnection sqlite_conn = new SQLiteConnection("Data Source = " + database + "; Version = 3; New = True; Compress = True;");
try
{
sqlite_conn.Open();
SQLiteCommand sqlite_cmd = sqlite_conn.CreateCommand();
sqlite_cmd.CommandText = "INSERT INTO AttendanceLog (UserID, DateTime, Action, Session) VALUES (" +
"'" + ID + "', '" + DateTime + "', '" + Action + "', '" + Session + "');";
sqlite_cmd.ExecuteNonQuery();
sqlite_conn.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
sqlite_conn.Close();
}
此代码只能由我绑定到按钮的此对话框表单访问:
private void buttonAccept_Click(object sender, EventArgs e)
{
if (textBoxID.Text.Length == 0 || textBoxTimeDate.Text.Length == 0)
{
MessageBox.Show("ID or Time and Date cannot be empty!", "Missing field", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (buttonAccept.Text.Equals("Accept"))
{
buttonAccept.Text = "Confirm!";
return;
}
DatabaseHandles.AddAttendance(textBoxID.Text, textBoxTimeDate.Text, comboBoxAction.Text, comboBoxSession.Text);
MessageBox.Show("Record added!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
DialogResult = DialogResult.OK;
}
如果我从我的 Form Kiosk 模式访问它,它工作得很好,但如果我从其他任何地方访问它,它会报告数据库已锁定。
奇怪的是,当我事先先进入我的 Kiosk 模式然后再访问其他地方时,它工作正常。
我不知道我错过了什么。这是我调用表单时使用的相同代码:
AttendanceInsert ai = new AttendanceInsert();
ai.ShowDialog();
不知道能不能解决你的问题,但没理由不做对:
public static void AddAttendance(string id, string datetime, string action, string session)
{
try
{
using (var sqlite_conn = new SQLiteConnection("Data Source = " + database + "; Version = 3; New = True; Compress = True;")
{
sqlite_conn.Open();
using (var sqlite_cmd = sqlite_conn.CreateCommand())
{
sqlite_cmd.CommandText = "INSERT INTO AttendanceLog (UserID, DateTime, Action, Session) VALUES (@Id, @DateTime, @Action, @Session);";
sqlite_cmd.Parameters.Add(new SqlParameter("@Id", SqlDbType.NVarChar)).Value = id;
sqlite_cmd.Parameters.Add(new SqlParameter("DateTime", SqlDbType.NVarChar)).Value = datetime;
sqlite_cmd.Parameters.Add(new SqlParameter("Action", SqlDbType.NVarChar)).Value = action;
sqlite_cmd.Parameters.Add(new SqlParameter("Session", SqlDbType.NVarChar)).Value = session;
sqlite_cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
这样您的连接和 SqlCommand 将被关闭和处理,并且您正在使用参数化查询,这将防止 SQL 注入。如果您的数据库列不是 NVarChar,则需要更改它。您还可以在参数定义中添加长度。
此锁定问题是由于尝试 re-use 数据库连接而引起的。 ADO.Net 有一个称为连接池的功能,这样 re-using 在整个应用程序中使用相同的连接对象是一个 非常糟糕的主意 这实际上会使事情变得 更慢,除了导致像这样的锁定问题。
相反,您确实应该为大多数查询创建一个全新的连接对象,在 运行 查询之前立即打开连接,并尽快 处理 对象当查询完成时。最简单的方法是通过 using
块:
private static string ConnectionString {get;} = "Data Source = " + database + "; Version = 3; New = True; Compress = True;"
public static void AddAttendance(string ID, string dateTime, string Action, string Session)
{
string sql = "INSERT INTO AttendanceLog (UserID, DateTime, Action, Session) VALUES (@id, @time, @action, @session);";
using (var conn = new SQLiteConnection(ConnectionString))
using (var cmd = new SQLiteCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@id", ID);
// Note the DateTime format. It's necessary to store date values in this way in order to preserve cardinality and allow indexes and date math to function properly.
// Better DB platforms would have a DateTime type to use instead
cmd.Parameters.AddWithValue("@time", DateTime.Parse(dateTime).ToString("yyyy-MM-dd HH:mm:ss"));
cmd.Parameters.AddWithValue("@action", Action);
cmd.Parameters.AddWithValue("@session", session);
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
using
块将保证 正确关闭连接,即使在 sqllite_conn.Close()
调用可能被错过的情况下也是如此。
事情是这样的:修复这个查询是不够的!
您必须修复 ALL 的查询以依赖 using
块, 否则您仍然有风险用于锁定。锁定问题不会显示在实际导致锁定的查询上,而是在 运行 数据库被锁定后 的查询上显示。
在这种情况下,看起来锁定问题可能发生在您显示网格的位置。网格查询 运行 很有趣,没有显示任何错误,但也没有释放它的锁,因此您在尝试添加记录时遇到错误,因为这是之后的下一个查询。
参数也是如此。 仅此查询使用查询参数而不是字符串连接是不够的。你必须这样做每次你将数据替换为SQL字符串,在EVERY查询.
罪魁祸首是 reader,虽然我用 using
参数关闭了所有连接,但我忘记关闭我使用的所有 readers,我应该利用 using
这里也是,但你明白了。数据库已锁定 readers 未关闭
public static void LoadScannedUser(string ID)
{
string sql = "SELECT * FROM Users WHERE ID = '" + ID + "'";
using (var conn = new SQLiteConnection(ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
try
{
cmd.CommandText = sql;
conn.Open();
SQLiteDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
scannedUser.ID = reader.GetString(1);
scannedUser.Password = reader.GetString(2);
scannedUser.PPicture = Cryptorizer.Base64ToImage(reader.GetString(3));
scannedUser.FName = reader.GetString(4);
scannedUser.MName = reader.GetString(5);
scannedUser.LName = reader.GetString(6);
scannedUser.Agency = reader.GetString(7);
scannedUser.Position = reader.GetString(8);
}
reader.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}