Vb.net Winform Обновление GUI между синхронными вызовами SQL - PullRequest
1 голос
/ 15 июля 2011

тема Привет всем, я интенсивно искал это, но не смог найти результат, который действительно соответствует этой ситуации.

У меня (в настоящее время) однопоточное приложение, написанное на VB.Net. Основные этапы обработки - 12 проверенных элементов списка в древовидном элементе управления. Программа выполняет шаги (функции и хранимые процедуры) по очереди и проверяет результат, прежде чем продолжить.

Шаги (значительно упрощенные): 1. Импорт данных из текстового файла 2. Массовая вставка в базу данных 3. Сделайте некоторую обработку 4. Используйте эти данные, чтобы получить гораздо больше связанных данных по сети в сложном соединении со связанного сервера. 5. Обновите локальную базу данных с результатами 6. Делайте больше обработки 7. Вставьте окончательные результаты в другую базу данных на другом сервере.

Я не буду вдаваться во все причины, по которым это нужно было сделать, например (разные владельцы серверов в компании, отсутствие доверия между серверами, доступ только для чтения к определенным базам данных и т. Д.), Но проблема заключается в шаге 4 .

В зависимости от загрузки обработки на (стороннем) сервере и объема данных в файле импорта этот шаг может занять 1-1 / 2 часа. Поскольку это однопоточное приложение, графический интерфейс останавливается в ожидании получения данных со связанного сервера.

Помимо серого блока на рабочем столе (из-за отсутствия обновлений графического интерфейса), программа работает отлично.

Попытки решения: 1) Я попытался предложить таймер для обновления формы безуспешно.

2) Я пытался использовать фоновый рабочий процесс, но не смог заставить приложение ждать результата, прежде чем программа продолжила.

3) Я также пробовал разные примеры потоков, но безуспешно. Все остальные шаги выполняются так быстро, что графический интерфейс никогда не останавливается, но я бы не стал тратить время на все мои вызовы sql в этом приложении.

За все время, что я занимался этим, я впервые не смог найти решение в Интернете, и мне никогда не приходилось публиковать его ранее, поэтому помощь в этом вопросе будет принята с благодарностью.

- редактировать

Вот что я попробовал: Спасибо за быстрый ответ.

Я использовал описанный здесь процесс: http://midnightprogrammer.net/post/Using-Background-Worker-in-C.aspx (после преобразования его в vb.net). но программа проскочила сразу после шага (Дело № 6 внизу).

Imports System.Runtime.InteropServices
Imports System.Threading
Imports System.ComponentModel

Private Sub bgw_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs)
        'Report progress bar change     
        progress.Value = e.ProgressPercentage

    End Sub
Private Sub bgw_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
    If (e.Cancelled) Then
        WriteStatus("Operation Cancelled")
        bRetval = False
    Else
        WriteStatus("Operation Completed")
        bRetval = True
    End If

End Sub


'Background worker DoWork method. Here we will perform our heavy duty tasks. 
Private Sub bgw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)

    Dim Sql As String = "Get_ALS_Data"
    Dim cmd As New SqlCommand(Sql, ConnLocal)
    cmd.CommandTimeout = 9000 ' 2 and a half hours 
    Try
        Dim i As Integer = 0
        ConnLocal.Open()

        'ImportRowCount = cmd.ExecuteScalar()
        Dim dr As SqlDataReader
        dr = cmd.ExecuteReader()
        While dr.Read()
            i = i + 1
            'report to the backgroundworkerprogress.changed event of the background worker class
            bgw.ReportProgress(i)
            Thread.Sleep(1)
            'Call and check if the cancellation of the operation is pending. If returned true        
            'DoWorkEventArgs object cancels the operation.        
            If bgw.CancellationPending Then
                e.Cancel = True
                Return
            End If
            ImportRowCount = CInt(dr(0))
        End While

    Catch ex As Exception
        WriteStatus("Get ALS Data error: " & ex.Message)
        e.Cancel = True
        Return
    Finally
        ConnLocal.Close()
    End Try

    WriteStatus("Get ALS Data completed successfully.")
    e.Cancel = False
    Return


End Sub

Private Sub bgw_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs)
    'Report progress bar change     
    progress.Value = e.ProgressPercentage

End Sub

Private Sub bgw_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
    If (e.Cancelled) Then
        WriteStatus("Operation Cancelled")
        bRetval = False
    Else
        WriteStatus("Operation Completed")
        bRetval = True
    End If

End Sub

 Private Sub ProcessCheckedNodes(ByVal parentNode As TreeNode)
    Dim Success As Boolean = False
    For Each childNode As TreeNode In parentNode.Nodes
        ProgBar += BarSeg
        If ProgBar > 100 Then
            ProgBar = 100
        End If
        pb1.Value = ProgBar
        Me.Refresh()
        If childNode.Checked Then
            Select Case childNode.Name
                Case "Node1"
                    '1. Clear local work tables
                    SetChild(childNode, "True")
                    Success = DoCleanup()
                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If

                    SetChild(childNode, "False")
                Case "Node2"
                    '2. Clear Server Intake table
                    SetChild(childNode, "True")
                    Success = TruncateInserts("DoCleanUp")

                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")
                Case "Node3"
                    '3. Load the temp table
                    SetChild(childNode, "True")
                    Success = LoadMyTempTable()

                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")
                Case "Node4"
                    '4. Load the data from the temp table to the local database
                    SetChild(childNode, "True")
                    Success = BulkCopy_Intake()

                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")
                Case "Node5"
                    '5. Get Intake Dup's
                    SetChild(childNode, "True")
                    Success = GetIntakeDups()

                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")
                **Case "Node6"
                    '6. Get the matching data from the ALS database
                    SetChild(childNode, "True")
                    'Success = GetALS_Data()
                    bgw.RunWorkerAsync()
                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")**
                Case "Node7"
                    '7. Get Core Dup's
                    SetChild(childNode, "True")
                    Success = GetCoreDups()

                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")
                Case "Node8"
                    '8. Process
                    SetChild(childNode, "True")
                    Success = Process()

                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")
                Case "Node9"
                    '9. Export NotFound
                    SetChild(childNode, "True")
                    Success = ExportNotFound()

                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")
                Case "Node10"
                    '10. Move Inserts
                    SetChild(childNode, "True")
                    Success = MoveInserts()

                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")
                Case "Node11"
                    '11. Backup
                    SetChild(childNode, "True")
                    Success = Backup()

                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")
                Case "Node12"
                    SetChild(childNode, "True")
                    'Success = LoadDc()

                    If Success <> True Then
                        SetChild(childNode, "Error")
                        Cursor = Cursors.Default
                        Exit Sub
                    End If
                    SetChild(childNode, "False")
                Case Else
                    'Ignore  it
            End Select
        Else
            childNode.ImageIndex = 0
            childNode.SelectedImageIndex = 0
        End If
        ProcessCheckedNodes(childNode)
    Next
    pb1.Value = 100
End Sub**strong text**


Public Function GetALS_Data() As Boolean

    'refresh the form while waiting for the ALS data
    Dim sAcct As String = ""

    Dim Sql As String = "Get_ALS_Data"
    Dim cmd As New SqlCommand(Sql, ConnLocal)
    cmd.CommandTimeout = 9000 ' 2 and a half hours 
    Try
        ConnLocal.Open()
        ImportRowCount = cmd.ExecuteScalar()
    Catch ex As Exception
        WriteStatus("Get ALS Data error: " & ex.Message)
        Return False
    Finally
        ConnLocal.Close()
    End Try

    WriteStatus("Get ALS Data completed successfully.")
    Return True

End Function

1 Ответ

1 голос
/ 15 июля 2011

Не знаю, почему ваш фоновый поток не работает для вас, но если у вас возникли проблемы по той или иной причине, вы можете попробовать это решение. Может быть, немного излишним для того, что вам нужно, но это будет работать.

Итак, вы можете захотеть изучить шаблон синхронизации с использованием процесса Syncevents и очереди. Если бы вы поместили все свои элементы в модель FIFO очереди и использовали их из фонового потока, вы могли бы делать все, о чем вы говорите, и поддерживать удобный пользовательский интерфейс, сделать его отменяемым и т. Д. Хорошим преимуществом для этого является отсутствие тайм-аутов, кроме во время звонков из вашей базы данных или других предметов, но не потока.

Мой код на C #, но вы можете получить изображение. Вы также можете посмотреть на это для получения информации о процессе EventHandle: MSDN - EventWaitHandle

Я не проверял это, поскольку я только что набрал его, однако мы используем этот тип модели внутренней очереди в приложениях, с которыми я работаю, и он очень хорошо работает для обработки данных в фоновом режиме и поддержания отзывчивого пользовательского интерфейса в течение длительного времени. и отменяемые операции. Если вы хотите отменить операцию, просто установите ExitThreadEvent. Надеюсь, это даст вам несколько дополнительных путей.

Создать класс SyncEvent

public class SyncEvents
{
    private EventWaitHandle _newItemEvent;

private EventWaitHandle _exitThreadEvent;
private WaitHandle[] _eventArray;

public SyncEvents()
{
    _newItemEvent = new AutoResetEvent(false);
    _exitThreadEvent = new ManualResetEvent(false);
    _eventArray = new WaitHandle[2];
    _eventArray[0] = _newItemEvent;
    _eventArray[1] = _exitThreadEvent;
}

public EventWaitHandle ExitThreadEvent
{
    get { return _exitThreadEvent; }
}

public EventWaitHandle NewItemEvent
{
    get { return _newItemEvent; }
}

public WaitHandle[] EventArray
{
    get { return _eventArray; }
}
}

Используя объект Queue, вы можете создать класс инструкций, чтобы выполнять свою работу, и создать класс, подобный этому, с вовлечением потоков также.

public class RunItem
{

public int SequenceToRun { get; set; }

}

public class ItemRunner
{

private SyncEvents _syncEvents;
private Queue<RunItem> _items;
private Thread _processThread;

public ItemRunner(SyncEvents events, Queue<RunItem> items)
{
   this._syncEvents = events;
   this._items = items;
}

public void Start()
{
   this._processThread = new Thread(this.Run);
   this._processThread.IsBackground = true;
   this._processThread.Start();
}

private void Run()
{
   try
   {
            while (WaitHandle.WaitAny(_syncEvents.EventArray) != 1)
            {
                RunItem item = null;

                lock (this._items)
                {
                    if (this._items.Count > 0)
                    {
                        item = this._items.Dequeue();
                        if (item != null)
                        this.ProcessItem(item);
                        this._syncEvents.NewItemEvent.Set();
                    }
                    else
                    {
                      this._syncEvents.ExitThreadEvent.Set();
                    }

                }
            }
   }
   catch (Exception ex)
   {
      // do something to log your exception here
      // you should have the try catch since you are running in a thread and if it 
      // throws an exception it could kill your entire app.

   }
}

private void ProcessItem(RunItem item)
{
      // Do your item processing here.
   // You could have a final item that executes the 
  // this._syncEvents.ExitThreadEvent.Set(); so that it actually will stop waiting 

}
}
...