诊断SQLite锁定的原因

时间:2015-03-17 11:36:38

标签: vb.net sqlite

在vb.net应用程序中,我遇到一种情况,表单中某个部分的用户操作正在创建一个SQLite锁定,导致应用程序稍后出现问题(在这种情况下,将其关闭)。

以下是用户将数据添加到要打印的项目列表时调用的子例程:

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

查询触及的数据库中唯一的表是tblImages。即使这样,连接也会在完成后关闭。

关闭表单时,会调用一个函数来捕获写入“字典”的数据,并在加载时将其重新插入到数据库中。

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

此函数在dbLocalQuery.ExecuteNonQuery()阶段立即失败并出现数据库锁定错误,但我无法理解原因。我没有正确处理我的数据库关闭吗?我的印象是SQLite只在写入时创建锁定,这在所描述的显式情况下永远不会发生。

如果我没有“添加”项目到打印队列(程序的基本功能),程序将正常关闭。

2 个答案:

答案 0 :(得分:2)

执行后处理每个命令应该解决问题

dbLocalQuery.ExecuteNonQuery()
dbLocalQuery.Dispose()

问题是因为你有删除和插入命令同时执行导致错误

            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()

参考: http://www.sqlite.org/cvstrac/wiki?p=DatabaseIsLocked

答案 1 :(得分:0)

我通过用本地系统注册表存储替换数据库来解决这个问题。这有利于使用户不可知。数据库设置用作默认值'对于新用户。

的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