Results 1 to 5 of 5

Thread: [RESOLVED] BackgroundWorker References Old Value

  1. #1

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,893

    Resolved [RESOLVED] BackgroundWorker References Old Value

    I have a Windows Form Application that uses a background worker to essentially continuously read from a PLC. The way it does this is it gets rows from a strongly-typed DataSet, reads from the PLC, and then displays the value back to the user. Here is a simplified version of the code:
    Code:
    Private Async Sub BackgroundWorkerReadPlc_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorkerReadPlc.DoWork
        While Not BackgroundWorkerReadPlc.CancellationPending
            ReadAlerts()
            ReadCurrentPlc()
        End While
    End Sub
    
    Private Sub ReadCurrentPlc()
        Dim currentPlcId = GetPlcId()
        Dim currentPlc = My.Application.Database.PLCs.SingleOrDefault(Function(plc) plc.Id.Equals(currentPlcId))
        Dim currentPlcTags = My.Application.Database.PlcTags.Where(Function(tag) tag.PlcId.Equals(currentPlc.Id))
        Dim addresses = currentPlcTags.Where(Function(tag) Not String.IsNullOrWhiteSpace(tag.Address)).Select(Function(tag) tag.Address)
        Dim readAddresses = currentIPlc.Read(addresses)
    
       Invoke(Sub()
                  If (BackgroundWorkerReadPlc.CancellationPending) Then
                      Return
                  End If
                  WebView2.CoreWebView2.ExecuteScriptAsync($"dispatchMessageReceivedEvent({readAddresses});")
              End Sub)
    End Sub
    This works fine if I spin when I first spin up my application, but as soon as I update the underlying My.Application.Data.PlcTags table to create a new row, the subsequent ReadCurrentPlc calls reference the old state of the strongly typed DataSet.

    I've tried calling Copy on the first line in my while loop and passing the (non-strongly typed) DataSet values to the methods, but this doesn't work either.

    My best guess is that because the UI thread is updating the data that the background thread never recognizes that it has been updated. The problem is that I don't know how to address it.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,604

    Re: BackgroundWorker References Old Value

    Can you create the simplest possible complete example that demonstrates the issue? For instance, is the WebView2 relevant to the issue at all? Can you use an untyped DataSet in the same form as that code?

  3. #3

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,893

    Re: BackgroundWorker References Old Value

    I was able to resolve my issue, but I want to leave this open in case there is a better solution.

    After I update the underlying rows that can modify the values read in the BGW I'm calling:
    Code:
    Private Sub RestartBackgroundWorkerReadPlc()
        Try
            ' This works for my use case because I only ever have 1 form
            Dim activeForm = DirectCast(Application.OpenForms.Item(0), FormMain)
    
            activeForm.BackgroundWorkerReadPlc.CancelAsync()
            Do While Not activeForm.BackgroundWorkerReadPlc.IsBusy
                ' HACK: bad practice, but cannot think of anything else
                Application.DoEvents()
            Loop
            activeForm.BackgroundWorkerReadPlc.RunWorkerAsync()
        Catch
            LogError("Unable to restart the PLC reader, the value will not be updated until it can be restarted.")
        End Try
    End Sub
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  4. #4

    Thread Starter
    Super Moderator dday9's Avatar
    Join Date
    Mar 2011
    Location
    South Louisiana
    Posts
    11,893

    Re: BackgroundWorker References Old Value

    Actually as an update, this doesn't always work. I'm a little surprised but occasionally it will throw an exception on the RunWorkerAsync and what is odd is that IsBusy returns true so I'm not sure why it exits my Do/While loop.

    Edit, because I'm an idiot and almost always mix up boolean logic. I took out the Not. This fixes that issue but it turns out that it doesn't actually solve my problem. The data set is still stale.

    Edit #2 - I have isolated my issue, but I don't know how to resolve it. My property defined in My.Application is setup as a singleton:
    Code:
    Private _database As MyStronglyTypedDataSet
    Public ReadOnly Property Database As MyStronglyTypedDataSet
        Get
            If (_database Is Nothing) Then
                _database = GetOrCreateDatabase()
            End If
            Return _database
        End Get
    End Property
    I noticed that when the Application starts that the If statement evaluates to true and any subsequent hits evaluate to false. However, when my background worker gets the property it is evaluating to true too, despite having already been created in the application start.

    Update #3 - Changing the property, private variable, and method called in the getter appears to have force it to only call the method once. This messes with some of my other logic, but at least this issue is resolved.
    Last edited by dday9; Jun 20th, 2024 at 10:58 PM.
    "Code is like humor. When you have to explain it, it is bad." - Cory House
    VbLessons | Code Tags | Sword of Fury - Jameram

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,604

    Re: [RESOLVED] BackgroundWorker References Old Value

    That seems to suggest that My.Application is thread-specific, i.e. the My.Application property returns a different object on each thread. The solution would be to make your '_database' field Shared. That way, it doesn't matter which object you get the Database property on, because it will always be accessing the same field and thus the same MyStronglyTypedDataSet object. You would want to add a SyncLock statement to the property getter though, to make sure two threads don't try to create a DataSet at the same time.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width