【问题标题】:Keep GUI responsive during SQL Backup在 SQL 备份期间保持 GUI 响应
【发布时间】:2012-12-07 17:26:31
【问题描述】:

VB.NET 2.0:

我正在从 VB.NET 应用程序向 SQL 服务器发出备份命令。我正在捕获它发出的消息并将它们附加到多行文本框。

不过,我想做的是让应用程序继续响应 GUI 控制事件(主要是为了让用户可以调整输出窗口的大小或取消备份)。所以我使用BeginExecuteReader 并与Application.DoEvents() 循环旋转。但似乎只要备份开始发出它的打印语句,我就会得到IAsyncResult.IsCompleted = True,它会下降到EndExecuteReader,并且GUI 现在又被锁定了。我怎样才能让它留在循环中,直到备份命令完成,但仍然得到那些输出语句并保持 GUI 响应?谢谢。

这是我的代码:

' Enable retrieve print statements from the server'
AddHandler oConn.InfoMessage, AddressOf LogToBufferHandler

strSQL &= vbNewLine & "begin try"
strSQL &= vbNewLine & " declare @BackupName as varchar(255)"
strSQL &= vbNewLine & " declare @BackupDesc as varchar(255)"
strSQL &= vbNewLine & " declare @backupTime as varchar(50)"
strSQL &= vbNewLine & " set @backupTime = (select convert(datetime, getdate(), 100))"
strSQL &= vbNewLine & " set @BackupName = (SELECT '[' + db_name() + '] Full Backup')"
strSQL &= vbNewLine & " set @BackupDesc = (SELECT 'Automated full backup of [' + db_name() + '] on ' + @backupTime + '.')"
strSQL &= vbNewLine & " "
strSQL &= vbNewLine & " BACKUP DATABASE [#Database#]"
strSQL &= vbNewLine & " TO DISK = @BackupFullPath"
strSQL &= vbNewLine & " WITH stats,"
strSQL &= vbNewLine & "      NAME = @BackupName,"
strSQL &= vbNewLine & "      DESCRIPTION = @BackupDesc;"
strSQL &= vbNewLine & " select [IsSuccessful] = 1"
strSQL &= vbNewLine & " end try"
strSQL &= vbNewLine & " begin catch"
strSQL &= vbNewLine & " SELECT [IsSuccessful] = 0"
strSQL &= vbNewLine & " end catch"

'Workaround: Backup Database requires the name of the object, not a string'
'            and I dont want to use dynamic SQL.'
strSQL = strSQL.Replace("#Database#", sb.InitialCatalog)

oConn.Open()
oCmd = New SqlCommand()
oCmd.Connection = oConn
oCmd.CommandText = strSQL
oCmd.CommandType = CommandType.Text
oCmd.Parameters.AddWithValue("@BackupFullPath", backupFullPath)
oCmd.CommandTimeout = 60 * 5

'Spin until complete, cancel, or timeout'
Dim result As IAsyncResult = oCmd.BeginExecuteReader(CommandBehavior.CloseConnection)
While Not result.IsCompleted
    Application.DoEvents()
    If blnCancel Then
        oCmd.Cancel()
    End If
    System.Threading.Thread.Sleep(50)
End While
Try
    oDataReader = oCmd.EndExecuteReader(result)
    oDataTable.Load(oDataReader)

    'Get results'
    ' (unfourtunately, you cannot do BeginExecuteScalar ASync in .Net 2.0,'
    ' so we are using a DataTable first column, row)'
    If oDataTable IsNot Nothing _
    AndAlso oDataTable.Rows.Count > 0 _
    AndAlso oDataTable.Columns.Contains("IsSuccessful") _
    AndAlso oDataTable.Rows(0).Item("IsSuccessful") = 1 Then
        eBackupResult = BackupStatus.Succeeded
        returnPath = backupFullPath 'Only set return path if the backup succeeded'
    Else
        eBackupResult = BackupStatus.Failed
    End If
Catch ex As Exception
    If Not ex.Message.Contains("cancelled by user") Then Throw ex
    eBackupResult = BackupStatus.Canceled
End Try

【问题讨论】:

  • stackoverflow 的语法高亮似乎不喜欢 VB,哇...对不起大家
  • 谢谢同样的答案,一文不值

标签: vb.net tsql asynchronous database-backups


【解决方案1】:

轮询循环 (DoEvents) 被认为是邪恶的,原因有很多。这可能是您切换到BackgroundWorker 并放弃讨厌的 Begin-poll-End 方案的最简单方法。

如果您想保留它,这里有一个错误:接受阅读器可能很快,但这并不意味着所有结果都已经到达(让我说明一下:如果您查询了 1TB 的数据怎么办?-阅读即一个漫长的过程)。您也需要以异步(轮询)方式Read。现在事情已经失控了。放弃吧。

换句话说,oDataTable.Load 正在阻塞,很难修复。

【讨论】:

  • 所以在这种情况下读取 BeginASync 会让 UI 保持响应,直到第一个结果开始流式传输?一旦他们流式传输,然后由我来处理结果?
  • 它在第一个结果到达时完成(这是有道理的——也许用户现在想要第一个结果,或者有 TB 的数据使得缓冲不可能)。就像我说的,你也需要异步读取。
【解决方案2】:

您可以为此使用任务。任务在概念上类似于线程。通过将代码放入任务中,主线程可以继续运行(该线程接受用户输入并响应调整大小命令)。

这是一篇更多地讨论任务的文章,并有一个 VB.NET 示例。

http://www.dotnetcurry.com/ShowArticle.aspx?ID=491

玩得开心!

【讨论】:

  • 对不起,我应该提到这个:VB.NET 2.0。
【解决方案3】:

BackGroundWorker 和 ReportProgress

BackgroundWorker Class

【讨论】:

  • 是的,但是 BeginExecuteReader 的优点是复杂性较低,因为它都在 GUI 线程上。
  • OK 不那么复杂并挂起。当您想要响应式 GUI 时,在 GUI 线程上是件好事吗?
猜你喜欢
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多