我正在尝试实现一个异步搜索“引擎”,但是我遇到了一些困难。
由于某种原因,偶尔会抛出一次SqlException,表明:
“变量名称'@input'已经声明。变量名称在查询批处理或存储过程中必须是唯一的。”
样品申请
以下代码以sys.messages表为目标,因此您要做的就是更改连接字符串。
Public Class Form1
Public Sub New()
Me.InitializeComponent()
Me.input = New TextBox() With {.Dock = DockStyle.Top, .TabIndex = 0}
Me.output = New RichTextBox() With {.Dock = DockStyle.Fill, .TabIndex = 1, .ReadOnly = True, .WordWrap = False}
Me.Controls.AddRange({Me.output, Me.input})
End Sub
Private Sub Search(sender As Object, e As EventArgs) Handles input.TextChanged
Dim input As String = Me.input.Text
Static command As SqlCommand
Static source As CancellationTokenSource
If (Not command Is Nothing) Then command.Cancel()
If (Not source Is Nothing) Then source.Cancel()
command = New SqlCommand()
source = New CancellationTokenSource()
Task.Factory.StartNew(Sub() Me.SearchingAsync(input, command, source.Token))
End Sub
Private Sub SearchingAsync(input As String, command As SqlCommand, token As CancellationToken)
Dim [error] As Exception = Nothing
Dim cancelled As Boolean = False
Dim result As List(Of sys_message) = Nothing
Try
Using connection As New SqlConnection("Server=instance\name;Database=name;Trusted_Connection=True;")
connection.Open()
command.Connection = connection
command.CommandType = CommandType.Text
command.CommandText = "select * from sys.messages where [text] like '%' + @input + '%';"
command.Parameters.AddWithValue("@input", input)
Using reader As SqlDataReader = command.ExecuteReader()
result = New List(Of sys_message)()
Do While (reader.Read() AndAlso (Not token.IsCancellationRequested))
result.Add(New sys_message() With {
.message_id = CInt(reader.Item("message_id")),
.language_id = CInt(reader.Item("language_id")),
.severity = CInt(reader.Item("severity")),
.is_event_logged = CBool(reader.Item("is_event_logged")),
.text = CStr(reader.Item("text"))
})
Loop
End Using
End Using
cancelled = token.IsCancellationRequested
Catch ex As SqlException When ex.Message.ToLower().Contains("operation cancelled by user")
cancelled = True
Catch ex As ThreadAbortException
cancelled = True
Catch ex As OperationCanceledException
cancelled = True
Catch ex As Exception
[error] = ex
Finally
Me.Invoke(
Sub()
'If (String.CompareOrdinal(input, Me.input.Text) = 0) Then
If (Not [error] Is Nothing) Then
Me.output.Text = String.Concat("Input='", input, "', Output={Result: 'error', Type: '", [error].GetType.Name, "', Message: '", [error].Message.Replace(Environment.NewLine, " "), "'}", Environment.NewLine, Me.output.Text).Trim()
ElseIf (cancelled) Then
Me.output.Text = String.Concat("Input='", input, "', Output={Result: 'cancelled'}", Environment.NewLine, Me.output.Text).Trim()
Else
Me.output.Text = String.Concat("Input='", input, "', Output={Result: 'success', Count: ", result.Count, "}", Environment.NewLine, Me.output.Text).Trim()
End If
'End If
End Sub
)
End Try
End Sub
Private WithEvents input As TextBox
Private WithEvents output As RichTextBox
Private Class sys_message
Public message_id As Integer
Public language_id As Integer
Public severity As Integer
Public is_event_logged As Boolean
Public text As String
End Class
End Class
因为您无意间SqlCommand
在多次调用之间共享对象SearchingAsync
。我将删除试图处理这些外部代码的外部代码,而仅SearchingAsync
创建其自己的未共享实例。
同时,您可能要考虑使用SqlCommand
公开的异步API ,例如ExecuteReaderAsync
,它允许您将取消令牌传递给他们,以便所有取消都由您的单个取消令牌处理。
您还需要确保将正确的取消令牌传递给方法的正确调用:
Private Sub Search(sender As Object, e As EventArgs) Handles input.TextChanged
Dim input As String = Me.input.Text
Static source As CancellationTokenSource
If (Not source Is Nothing) Then source.Cancel()
source = New CancellationTokenSource()
Dim token = source.Token
Task.Factory.StartNew(Sub() Me.SearchingAsync(input, token))
End Sub
基本上,在这一行代码中:
Task.Factory.StartNew(Sub() Me.SearchingAsync(input, command, source.Token))
完成后,您所知道的就是,在将来的某个时间点,它将执行以下操作:
Me.SearchingAsync(input, command, source.Token)
在将来的那个时间点,它将SqlCommand
从command
变量中加载一个对象,然后调用SearchingAsync
(并且类似地,source
在此时加载)
但是,如果与此同时您的Search
方法又重新运行怎么办?它取消了command
和source
最初用于此方法调用的,并将其替换为新副本。并且计划在将来运行另一个任务SearchingAsync
。这两个调用最终引用了相同的command
对象,因此最终@input
给它添加了两次参数。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句