Reputation: 555
On a vb.net application, I have a case where a users actions in one portion of the form is creating a SQLite lock that causes problems later in the application (in this case, closing it down).
Here is the sub-routine called when users add data to a list of items to be printed:
Private Sub btnAdd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAdd.Click
' Add item to printQueue => regenerate listPrint.items
Dim queueItem As New Dictionary(Of String, String)
queueItem("quantity") = inputQuantity.Value.ToString
queueItem("description") = textDesc.Text
queueItem("sizeUK") = inputSize.Value.ToString.Replace(".5", "½").Replace(".0", "")
queueItem("sku") = listStyles.SelectedItem.ToString
queueItem("colour") = textColour.Text
queueItem("date") = textDateCode.Text
queueItem("name") = textName.Text
Try
queueItem("sizeEU") = sizeEU(inputSize.Value).ToString.Replace(".5", "½")
Catch ex As Exception
queueItem("sizeEU") = "??"
End Try
' US Size: M = UK + 1; F = UK + 1.5
queueItem("sizeUS") = (inputSize.Value + 1.5 - (chkSex.CheckState * 0.5)).ToString.Replace(".5", "½")
' Add the image data as a string
dbLocalQuery = New SQLiteCommand("SELECT * FROM tblImages WHERE id ='" & listStyles.SelectedItem.ToString & "'", dbLocal)
If dbLocal.State = ConnectionState.Closed Then
dbLocal.Open()
End If
Dim r As SQLiteDataReader = dbLocalQuery.ExecuteReader()
Try
r.Read()
queueItem("image") = byte2string((r("image")))
Catch ex As Exception
queueItem("image") = settings("imgNotFound")
Finally
If dbLocal.State = ConnectionState.Open Then
dbLocal.Close()
End If
End Try
printQueue.Add(printQueue.Count + 1, queueItem)
MsgFrame.Items.Add(printQueue(printQueue.Count)("sku") & " x" & printQueue(printQueue.Count)("quantity"))
MsgFrame.SelectedIndex = MsgFrame.Items.Count - 1
' Update print queue list
populateList()
End Sub
The only table in the database touched by the query is tblImages. Even then, the connection is closed once finished.
When closing the form down, a function is called that captures data that is written to the Dictionary called Settings on load and reinserts it into the database.
Public Function dbLocalSave() As Boolean
'Recreates the tblSettings from current values stored in settings
Try
If dbLocal.State = ConnectionState.Closed Then
dbLocal.Open()
End If
If dbLocal.State = ConnectionState.Open Then
MsgFrame.Items.Add("Flushing local settings table")
MsgFrame.SelectedIndex = MsgFrame.Items.Count - 1
For Each pair In settings
Debug.Print("+ tblSettings: " & pair.Key & " = " & pair.Value)
dbLocalQuery = New SQLiteCommand("DELETE FROM tblSettings where name = '" & pair.Key & "';", dbLocal)
dbLocalQuery.ExecuteNonQuery()
dbLocalQuery = New SQLiteCommand("INSERT INTO tblSettings (`name`,`value`) VALUES ('" & pair.Key & "','" & pair.Value & "');", dbLocal)
dbLocalQuery.ExecuteNonQuery()
Next
Return True
Else
Return False
End If
Catch sqlex As SQLiteException
MessageBox.Show(sqlex.Message, "SQL Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Finally
If dbLocal.State = ConnectionState.Open Then
dbLocal.Close()
End If
End Try
End Function
This Function immediately fails at the dbLocalQuery.ExecuteNonQuery()
stage with a db lock error, but I cannot understand why. Am I not handling my db closing correctly? I was under the impression that SQLite only created locks on writes, which never happens in the explicit case described.
If I do not 'Add' an item to the print queue (fundamental functionality of the program), the program closes gracefully.
Upvotes: 0
Views: 1133
Reputation: 2318
Dispose each command after executing should solve the problem
dbLocalQuery.ExecuteNonQuery()
dbLocalQuery.Dispose()
the problem is because you have delete and insert command concurrently executing which is causing the error
dbLocalQuery = New SQLiteCommand("DELETE FROM tblSettings where name = '" & pair.Key & "';", dbLocal)
dbLocalQuery.ExecuteNonQuery()
'insert here '
dbLocalQuery.dispose()
dbLocalQuery = New SQLiteCommand("INSERT INTO tblSettings (`name`,`value`) VALUES ('" & pair.Key & "','" & pair.Value & "');", dbLocal)
dbLocalQuery.ExecuteNonQuery()
'here also '
dbLocalQuery.dispose()
Ref : http://www.sqlite.org/cvstrac/wiki?p=DatabaseIsLocked
Upvotes: 2
Reputation: 555
I worked around the issue by replacing the database writing with local system registry storage. This has the benefit of making it user agnostic. The db settings are used as a 'default' for new users.
onload:
'Get settings from Registy - if key not present, get defaults from db.
Dim regkey As RegistryKey = Registry.CurrentUser.OpenSubKey("Software\Foot Shop Ltd\" & Application.ProductName, True)
If regkey Is Nothing Then
'Key doesn't exist, populate settings from DB
Try
Debug.Print("Opening Database...")
If dbLocal.State = ConnectionState.Closed Then
dbLocal.Open()
End If
Debug.Print("Database Open - " & Application.StartupPath & "\boxLabels.db")
dbLocalQuery = New SQLiteCommand("SELECT * FROM tblSettings", dbLocal)
'Get default settings from SQLite DB, write to registry
Dim r As SQLiteDataReader = dbLocalQuery.ExecuteReader()
While r.Read()
settings.Add(CStr(r("name")), CStr(r("value")))
Debug.Print("Default Used: " & CStr(r("name")) & " = " & CStr(r("value")))
End While
r.Close()
Catch sqlex As SQLiteException
Debug.Print(sqlex.Message)
Catch ex As Exception
MessageBox.Show(ex.Message, "Loading Exception", MessageBoxButtons.OK, MessageBoxIcon.Error)
Application.Exit()
Exit Sub
Finally
If dbLocal.State = ConnectionState.Open Then
dbLocal.Close()
End If
End Try
Else
'build settings from registry
Dim names As String() = regkey.GetValueNames
' Open the next subkey if any and call myself.
Dim value As String
Dim data As String
For Each value In names
data = regkey.GetValue(value, Nothing)
settings.Add(value.ToString, data.ToString)
Debug.Print("Reg Value Used: " & value.ToString & " = " & data.ToString)
Next
End If
regkey.Close()
dbLocalSave:
Public Function dbLocalSave() As Boolean
'Recreates the tblSettings from current values stored in settings
Try
MsgFrame.Items.Add("Flushing local settings table")
MsgFrame.SelectedIndex = MsgFrame.Items.Count - 1
For Each pair In settings
Debug.Print("+ tblSettings: " & pair.Key & " = " & pair.Value)
My.Computer.Registry.SetValue("HKEY_CURRENT_USER\Software\Foot Shop Ltd\" & Application.ProductName, pair.Key, pair.Value)
Next
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return False
End Try
Return True
End Function
Upvotes: 0