即使在处理所有命令后数据库仍被锁定
Database Locked even after disposing all commands
即使我已处理完所有命令,数据库已锁定错误仍出现。我正在尝试写入数据库,但它在底部的 INSERT 命令上失败了。我以前让它工作但由于某种原因它现在开始失败了。
Sub Btn_SubmitClick(sender As Object, e As EventArgs)
If MsgBox("Are you sure?",vbYesNo,"Submit?") = 7 Then
'returns to previous screen
Else
'commences insert into database
Rows = (dataGridView1.RowCount - 1)
While count < rows
'putting grid stuff into variables
DateStart = dataGridView1.Rows(Count).Cells(0).Value.ToString
DateEnd = dataGridView1.Rows(Count).Cells(2).Value.ToString 'note other way round
TimeStart = dataGridView1.Rows(Count).Cells(1).Value.ToString
TimeEnd = dataGridView1.Rows(Count).Cells(3).Value.ToString
TotalHours = dataGridView1.Rows(Count).Cells(4).Value.ToString
OccuranceNo = dataGridView1.Rows(Count).Cells(5).Value.ToString
'fetching reason ID for Storage
SQLcommand = SQLconnect.CreateCommand
SQLcommand.CommandText = "SELECT Reason_ID FROM Reasons WHERE Reason_Name = '" & dataGridView1.Rows(Count).Cells(6).Value.ToString & "'"
SQLreader = SQLcommand.ExecuteReader
ReasonID = SQLreader("Reason_ID")
SQLcommand.Dispose
'fetching site ID for storage
SQLcommand = SQLconnect.CreateCommand
SQLcommand.CommandText = "SELECT Site_ID FROM Sites WHERE Site_Name = '" & dataGridView1.Rows(Count).Cells(7).Value.ToString & "'"
SQLreader = SQLcommand.ExecuteReader
SiteID = SQLreader("Site_ID")
SQLcommand.Dispose
Oncall = dataGridView1.Rows(Count).Cells(8).Value.ToString
'increment counter
Count = Count + 1
'send to database
SQLcommand = SQLconnect.CreateCommand
SQLcommand.CommandText = "INSERT INTO Shifts (Staff_ID, Date_Shift_Start, Date_Shift_End, Time_Shift_Start, Time_Shift_End, Total_Hours, Occurance_No, Site_ID, On_Call_Req, Rate, Approved, Reason_ID) VALUES ('" & userID & "' , '" & DateStart &"' , '" & DateEnd & "' , '" & TimeStart & "' , '" & TimeEnd & "' , '" & TotalHours & "' , '" & OccuranceNo & "' , '" & SiteID & "' , '" & Oncall & "' , '"& "1" & "' , '" & "N" & "' , '" & ReasonID & "')"
SQLcommand.ExecuteNonQuery()
SQLcommand.Dispose
End While
MsgBox("Ok")
End If
End Sub
显示的代码中有几处需要更改。由于代码中声明了 Connection
、Command
或 Reader
对象中的 none,因此它们必须是您正在重用的全局对象。 不要那样做。
一个持久连接可能有原因,但查询本质上是非常具体的,因此尝试重用 DbCommand
和 DataReader
可能会适得其反。由于这些与 DbConnection
密切合作,因此可能会发生各种不好的事情。这意味着问题的根源可能是代码中的 任何地方 。
以下将循环通过 DGV 以插入任意多行。
Dim SQL = "INSERT INTO Sample (Fish, Bird, Color, Value, Price) VALUES (@f, @b, @c, @v, @p)"
Using dbcon As New SQLiteConnection(LiteConnStr)
Using cmd As New SQLiteCommand(SQL, dbcon)
dbcon.Open()
cmd.Parameters.Add("@f", DbType.String)
cmd.Parameters.Add("@b", DbType.String)
cmd.Parameters.Add("@c", DbType.String)
cmd.Parameters.Add("@v", DbType.Int32)
cmd.Parameters.Add("@p", DbType.Double)
Dim fishName As String
For Each dgvR As DataGridViewRow In dgv2.Rows
' skip the NewRow, it has no data
If dgvR.IsNewRow Then Continue For
' look up from another table
' just to shorten the code
userText = dgvR.Cells(0).Value.ToString()
fishName = dtFish.AsEnumerable().
FirstOrDefault(Function(f) f.Field(Of String)("Code") = userText).
Field(Of String)("Fish")
' or
'Dim drs = dtFish.Select(String.Format("Code = '{0}'", userText))
'fishName = drs(0)("Fish").ToString()
cmd.Parameters("@f").Value = fishName
cmd.Parameters("@b").Value = dgvR.Cells(1).Value
cmd.Parameters("@c").Value = dgvR.Cells(2).Value
cmd.Parameters("@v").Value = dgvR.Cells(3).Value
cmd.Parameters("@p").Value = dgvR.Cells(4).Value
cmd.ExecuteNonQuery()
Next
End Using
End Using
- 注意:与原始代码一样,没有 数据验证 - 也就是说,它假定他们键入的内容始终有效。这很少是一个好的假设。
- 代码实现
Using
块,它将声明和创建目标对象(dbCommands、连接)并在完成后处理它们。它们不能干扰其他地方的代码,因为它们只存在于那个块中。
- SQL 参数用于简化代码并指定数据类型。像现在这样连接 SQL 的一个副作用是 一切都作为字符串传递! 这对于无类型的 SQLite 来说可能非常糟糕。
- 我会避免在循环中触发多个查找查询。原始代码应该抛出一个
InvalidOperationException
,因为它从不从 DataReader
中抛出 Read
。
- 也许最好的方法是让
Sites
和 Reasons
成为 DGV 中用户可以看到任何文本的 ComboBox
列,而 ValueMember
已经可供代码存储。
- 答案中显示的另一种选择是用数据预加载
DataTable
,然后使用一些扩展方法来查找所需的值。
- 如果您 "must" 使用读取器,请在它们自己的
Using
块中实现它们。
- 使用了一个
For Each
循环,它提供了正在检查的实际行。
- 我会认真重新考虑将
startDateTime
之类的内容存储为单独的 Date
和 Time
字段的想法。
如果使用 DB Provider 对象,这些是基础知识。不确定重构显示的过程是否会解决任何问题,因为由于 DBProvider 对象保持打开状态且未处理,问题很可能出现在代码中的任何位置。
要检查的另一件事是您可能为数据库使用的任何 UI 管理器。在您单击“保存”或“写入”按钮之前,其中许多会累积更改。在此期间,有些数据库已被锁定。
最后,即使这里的代码更短更简单,使用 DataAdapter
和 DataTable
将允许 DGV 中的新数据自动更新数据库:
rowsAffected = myDA.Update(myDT)
学习如何以这种方式配置它可能需要 30 分钟。
即使我已处理完所有命令,数据库已锁定错误仍出现。我正在尝试写入数据库,但它在底部的 INSERT 命令上失败了。我以前让它工作但由于某种原因它现在开始失败了。
Sub Btn_SubmitClick(sender As Object, e As EventArgs)
If MsgBox("Are you sure?",vbYesNo,"Submit?") = 7 Then
'returns to previous screen
Else
'commences insert into database
Rows = (dataGridView1.RowCount - 1)
While count < rows
'putting grid stuff into variables
DateStart = dataGridView1.Rows(Count).Cells(0).Value.ToString
DateEnd = dataGridView1.Rows(Count).Cells(2).Value.ToString 'note other way round
TimeStart = dataGridView1.Rows(Count).Cells(1).Value.ToString
TimeEnd = dataGridView1.Rows(Count).Cells(3).Value.ToString
TotalHours = dataGridView1.Rows(Count).Cells(4).Value.ToString
OccuranceNo = dataGridView1.Rows(Count).Cells(5).Value.ToString
'fetching reason ID for Storage
SQLcommand = SQLconnect.CreateCommand
SQLcommand.CommandText = "SELECT Reason_ID FROM Reasons WHERE Reason_Name = '" & dataGridView1.Rows(Count).Cells(6).Value.ToString & "'"
SQLreader = SQLcommand.ExecuteReader
ReasonID = SQLreader("Reason_ID")
SQLcommand.Dispose
'fetching site ID for storage
SQLcommand = SQLconnect.CreateCommand
SQLcommand.CommandText = "SELECT Site_ID FROM Sites WHERE Site_Name = '" & dataGridView1.Rows(Count).Cells(7).Value.ToString & "'"
SQLreader = SQLcommand.ExecuteReader
SiteID = SQLreader("Site_ID")
SQLcommand.Dispose
Oncall = dataGridView1.Rows(Count).Cells(8).Value.ToString
'increment counter
Count = Count + 1
'send to database
SQLcommand = SQLconnect.CreateCommand
SQLcommand.CommandText = "INSERT INTO Shifts (Staff_ID, Date_Shift_Start, Date_Shift_End, Time_Shift_Start, Time_Shift_End, Total_Hours, Occurance_No, Site_ID, On_Call_Req, Rate, Approved, Reason_ID) VALUES ('" & userID & "' , '" & DateStart &"' , '" & DateEnd & "' , '" & TimeStart & "' , '" & TimeEnd & "' , '" & TotalHours & "' , '" & OccuranceNo & "' , '" & SiteID & "' , '" & Oncall & "' , '"& "1" & "' , '" & "N" & "' , '" & ReasonID & "')"
SQLcommand.ExecuteNonQuery()
SQLcommand.Dispose
End While
MsgBox("Ok")
End If
End Sub
显示的代码中有几处需要更改。由于代码中声明了 Connection
、Command
或 Reader
对象中的 none,因此它们必须是您正在重用的全局对象。 不要那样做。
一个持久连接可能有原因,但查询本质上是非常具体的,因此尝试重用 DbCommand
和 DataReader
可能会适得其反。由于这些与 DbConnection
密切合作,因此可能会发生各种不好的事情。这意味着问题的根源可能是代码中的 任何地方 。
以下将循环通过 DGV 以插入任意多行。
Dim SQL = "INSERT INTO Sample (Fish, Bird, Color, Value, Price) VALUES (@f, @b, @c, @v, @p)"
Using dbcon As New SQLiteConnection(LiteConnStr)
Using cmd As New SQLiteCommand(SQL, dbcon)
dbcon.Open()
cmd.Parameters.Add("@f", DbType.String)
cmd.Parameters.Add("@b", DbType.String)
cmd.Parameters.Add("@c", DbType.String)
cmd.Parameters.Add("@v", DbType.Int32)
cmd.Parameters.Add("@p", DbType.Double)
Dim fishName As String
For Each dgvR As DataGridViewRow In dgv2.Rows
' skip the NewRow, it has no data
If dgvR.IsNewRow Then Continue For
' look up from another table
' just to shorten the code
userText = dgvR.Cells(0).Value.ToString()
fishName = dtFish.AsEnumerable().
FirstOrDefault(Function(f) f.Field(Of String)("Code") = userText).
Field(Of String)("Fish")
' or
'Dim drs = dtFish.Select(String.Format("Code = '{0}'", userText))
'fishName = drs(0)("Fish").ToString()
cmd.Parameters("@f").Value = fishName
cmd.Parameters("@b").Value = dgvR.Cells(1).Value
cmd.Parameters("@c").Value = dgvR.Cells(2).Value
cmd.Parameters("@v").Value = dgvR.Cells(3).Value
cmd.Parameters("@p").Value = dgvR.Cells(4).Value
cmd.ExecuteNonQuery()
Next
End Using
End Using
- 注意:与原始代码一样,没有 数据验证 - 也就是说,它假定他们键入的内容始终有效。这很少是一个好的假设。
- 代码实现
Using
块,它将声明和创建目标对象(dbCommands、连接)并在完成后处理它们。它们不能干扰其他地方的代码,因为它们只存在于那个块中。 - SQL 参数用于简化代码并指定数据类型。像现在这样连接 SQL 的一个副作用是 一切都作为字符串传递! 这对于无类型的 SQLite 来说可能非常糟糕。
- 我会避免在循环中触发多个查找查询。原始代码应该抛出一个
InvalidOperationException
,因为它从不从DataReader
中抛出Read
。- 也许最好的方法是让
Sites
和Reasons
成为 DGV 中用户可以看到任何文本的ComboBox
列,而ValueMember
已经可供代码存储。 - 答案中显示的另一种选择是用数据预加载
DataTable
,然后使用一些扩展方法来查找所需的值。 - 如果您 "must" 使用读取器,请在它们自己的
Using
块中实现它们。
- 也许最好的方法是让
- 使用了一个
For Each
循环,它提供了正在检查的实际行。 - 我会认真重新考虑将
startDateTime
之类的内容存储为单独的Date
和Time
字段的想法。
如果使用 DB Provider 对象,这些是基础知识。不确定重构显示的过程是否会解决任何问题,因为由于 DBProvider 对象保持打开状态且未处理,问题很可能出现在代码中的任何位置。
要检查的另一件事是您可能为数据库使用的任何 UI 管理器。在您单击“保存”或“写入”按钮之前,其中许多会累积更改。在此期间,有些数据库已被锁定。
最后,即使这里的代码更短更简单,使用 DataAdapter
和 DataTable
将允许 DGV 中的新数据自动更新数据库:
rowsAffected = myDA.Update(myDT)
学习如何以这种方式配置它可能需要 30 分钟。