5

I am currently trying to pass progress (Using Iprogress and Progress) from a inner function to the main function. Everything works after the inner function completes. However, I am retrieving/downloading some data using EBay's API's (EBay's finding service which is an rest service that returns data in XML format) and need to pass progress reports from the inner function to the main one during the download of the XML data retrieved/data processing.

Here's the main function's code (A little starting to get overloaded but you get whats going on. commented for sanity.):

 Private Property a As Integer
        Get
            Return a_value
        End Get
        Set(value As Integer)
            a_value = value
            ' ReportProgress(value, Nothing)
        End Set
    End Property
    Private a_value As Integer
Public Async Function EvaluateItem(item As List(Of ItemInfo), evalprogress As IProgress(Of Integer), Optional ManualEvaluation As Boolean = False) As Task(Of List(Of EvaluationInfo))
    Dim notevaluatedcount As Integer = 0
    Dim iteminfo1 As ItemInfo = New ItemInfo
    Dim tmpiteminfo As List(Of ItemInfo) = New List(Of ItemInfo)
    Dim tmpiteminfo2 As List(Of ItemInfo) = New List(Of ItemInfo)
    Dim pricefunctions As ItemValuation = New ItemValuation
    Dim tmpevalinfo As EvaluationInfo = New EvaluationInfo
    Dim itemevalresultarr As List(Of EvaluationInfo) = New List(Of EvaluationInfo)
    Dim mode As Double
    Dim avg As Double
    Dim min As Double
    Dim max As Double
    Dim standarddev As Double
    Dim pricearr() As Double
    Dim autoitem As List(Of EvaluationInfo) = New List(Of EvaluationInfo)
    Dim itemse As New ItemInformation
    ' Dim itemsnoteval As New ItemInformation
    ' itemseval.itemsevaluated = New List(Of String)
    ' itemsnoteval.notevaluated = New List(Of String)
    Dim tmpnoteval As List(Of ItemInfo) = New List(Of ItemInfo)

    Dim ran As Boolean
    'sort and store item condition information in evaluation table in database or by item.sort
    If item.Count > 1 And ManualEvaluation = True Then
        tmpiteminfo = SortByName(item)
        Await ClearEvalTable()
        tmpiteminfo2 = SortByCondition(tmpiteminfo)
        If CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess Then
            Await Task.Run(Function()

                               'Await Task.Delay(1000)
                               If evalprogress IsNot Nothing Then
                                   'ReportProgress(4, Nothing)
                                   evalprogress.Report(4)
                                   evalprogress.Report(5)
                               End If
                               ' tempCount += 1

                               Return 5
                           End Function)
        ElseIf CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess = False Then
            Await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Function()

                                                                                                             'Await Task.Delay(1000)
                                                                                                             If evalprogress IsNot Nothing Then
                                                                                                                 evalprogress.Report(4)
                                                                                                                 evalprogress.Report(5)
                                                                                                             End If
                                                                                                             ' tempCount += 1

                                                                                                             Return 5
                                                                                                         End Function)

        End If
        'evalprogress.Report(5)
        ' MsgBox("condition count:" + tmpiteminfo2.Count.ToString)
    ElseIf item.Count > 1 And ManualEvaluation = False Then
        'do nothing
    ElseIf item.Count = 1 And ManualEvaluation = True Then
        'change this code to perform manual evaluation on one item.
        'tmpiteminfo.Add(item.Item(0))
    End If
    ReDim pricearr(item.Count)

    If ManualEvaluation Then
        If ran = False Then
            Dim count As Integer
            For Each d As ItemInfo In tmpiteminfo
                count = count + 1
                If ItemValidation.ContainsValidCharacters(d.name) And ItemValidation.IsDouble(d.price) Then
                    'decide whether to manually evaluate the item or automatically evaluate it.
                    Log("Item name: " + d.name + " price #: " + d.price.ToString)
                Else
                    Log("Item: " + d.name + " has invalid characters in its name. It will not be evaluated. Please remove the invalid characters to have the item evaluated." + "The item price may not be an integer. please fix this too (if applicable)")
                    iteminfo1.name = d.name
                    iteminfo1.price = 0
                    iteminfo1.evaluationSource = d.evaluationSource
                    iteminfo1.damaged = "NO"
                    iteminfo1.aquisitiondate = "1/1/1111"
                    iteminfo1.itempicture = Nothing
                    ' tmpnoteval.Add(iteminfo1)
                    tmpevalinfo.currentItem = iteminfo1.name
                    tmpevalinfo.evaluated = False
                    tmpevalinfo.evaluationcondition = d.itemcondition
                    itemevalresultarr.Add(tmpevalinfo)
                    '  tmpnoteval.Add(iteminfo1)
                    notevaluatedcount += 1
                End If
                Select Case d.itemcondition
                    Case Is = ItemCondition.Broken
                        broken.Add(d.price)
                    Case Is = ItemCondition.LooksLikeNew
                        Lookslikenew.Add(d.price)
                        'MsgBox("Looks like new price:" + d.price.ToString)
                    Case Is = ItemCondition.SomewhatUsed
                        somewhatused.Add(d.price)
                        '  MsgBox("Somewhat Used price:" + d.price.ToString)
                    Case Is = ItemCondition.SomeDamage
                        damaged.Add(d.price)
                End Select
            Next
            ran = True
        End If

    Else
        'Automatically evaluate item using different retailers i.e. Amazon, E-Bay, etc.
        'use grabfromonlinesource multiple times to grab from each evaluation source used.
        Dim tmpevalitem As Task(Of List(Of EvaluationInfo))

        For i = 0 To item.Count - 1
            'does once for each item 
            'need to split it into conditions inside of grabfromonlinesource
            If ItemValidation.ContainsValidCharacters(item.Item(i).name) Then

                '  evalprogress = New Progress(Of Integer)(Function(value) InlineAssignHelper(value, a))
                If CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess Then
                    Await Task.Run(Function()

                                       'Await Task.Delay(1000)
                                       If evalprogress IsNot Nothing Then
                                           ' ReportProgress(10, Nothing)
                                           evalprogress.Report(10)
                                           ReportProgress(10, Nothing)
                                       End If
                                       ' tempCount += 1

                                       Return 10
                                   End Function)
                ElseIf CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess = False Then
                    Await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Function()

                                                                                                                     'Await Task.Delay(1000)
                                                                                                                     If evalprogress IsNot Nothing Then
                                                                                                                         evalprogress.Report(10)
                                                                                                                     End If
                                                                                                                     ' tempCount += 1

                                                                                                                     Return 10
                                                                                                                 End Function)

                End If

                tmpevalitem = GrabFromOnlineSource(item.Item(i).name, item.Item(i).evaluationSource, New Progress(Of Integer)(Function(value) InlineAssignHelper(a, value)))
                tmpevalitem.Wait()
                If CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess Then
                    Await Task.Run(Function()

                                       'Await Task.Delay(1000)
                                       If evalprogress IsNot Nothing Then
                                           ' ReportProgress(70, Nothing)
                                           evalprogress.Report(70)
                                       End If
                                       ' tempCount += 1

                                       Return 70
                                   End Function)
                ElseIf CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess = False Then
                    Await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, Function()

                                                                                                                     'Await Task.Delay(1000)
                                                                                                                     If evalprogress IsNot Nothing Then
                                                                                                                         'evalprogress.Report(70)
                                                                                                                     End If
                                                                                                                     ' tempCount += 1

                                                                                                                     Return 70
                                                                                                                 End Function)

                End If
                ' ItemValuation.tmpProgressIndicator.Report(60)


                If tmpevalitem.Result IsNot Nothing Then
                    autoitem.AddRange(tmpevalitem.Result)
                Else
                    Continue For
                End If
            Else
                Dim tmpevalinfoauto As EvaluationInfo
                tmpevalinfoauto.currentItem = item.Item(i).name
                tmpevalinfoauto.evaluationcondition = item.Item(i).itemcondition
                autoitem.Add(tmpevalinfoauto)
            End If
        Next i
        autoitem.Add(Nothing) 'use nothing as an indicator of a new item.
    End If

    If ManualEvaluation Then
        Dim done(4) As Boolean
        done(0) = False
        done(1) = False
        done(2) = False
        done(3) = False
        'MsgBox("item information count: " + tmpiteminfo2.Count.ToString)
        For f = 0 To tmpiteminfo2.Count - 1
            'MsgBox(tmpiteminfo2.Item(f).evaluationSource)
            Select Case tmpiteminfo2.Item(f).evaluationSource
                Case Is = "Gamestop"
                    tmpevalinfo.valuationsourceid = 1
                Case Is = "Amazon"
                    tmpevalinfo.valuationsourceid = 2
                Case Is = "Ebay"
                    tmpevalinfo.valuationsourceid = 3
                    'add more sources here later
            End Select
            Select Case tmpiteminfo2.Item(f).itemcondition
                Case Is = ItemCondition.Broken
                    'If done(0) = False Then
                    mode = pricefunctions.Mode(broken.ToArray)
                    max = pricefunctions.MaxValue(broken.ToArray)
                    min = pricefunctions.MinValue(broken.ToArray)
                    avg = pricefunctions.AveragePrice(broken.ToArray)
                    standarddev = pricefunctions.standarddeviation(broken.ToArray)
                    done(0) = True
                   ' End If
                Case Is = ItemCondition.LooksLikeNew
                    'If done(1) = False Then
                    mode = pricefunctions.Mode(Lookslikenew.ToArray)
                    max = pricefunctions.MaxValue(Lookslikenew.ToArray)
                    min = pricefunctions.MinValue(Lookslikenew.ToArray)
                    avg = pricefunctions.AveragePrice(Lookslikenew.ToArray)
                    standarddev = pricefunctions.standarddeviation(Lookslikenew.ToArray)
                    done(1) = True
                   ' End If
                Case Is = ItemCondition.SomeDamage
                    ' If done(2) = False Then
                    mode = pricefunctions.Mode(damaged.ToArray)
                    max = pricefunctions.MaxValue(damaged.ToArray)
                    min = pricefunctions.MinValue(damaged.ToArray)
                    avg = pricefunctions.AveragePrice(damaged.ToArray)
                    standarddev = pricefunctions.standarddeviation(damaged.ToArray)
                    done(2) = True
                   ' End If
                Case Is = ItemCondition.SomewhatUsed
                    'If done(3) = False Then
                    mode = pricefunctions.Mode(somewhatused.ToArray)
                    max = pricefunctions.MaxValue(somewhatused.ToArray)
                    min = pricefunctions.MinValue(somewhatused.ToArray)
                    avg = pricefunctions.AveragePrice(somewhatused.ToArray)
                    standarddev = pricefunctions.standarddeviation(somewhatused.ToArray)
                    done(3) = True
                    ' End If

            End Select

            'assign values to minimum price, maximum price, and the other prices. 
            If min > 0 Then
                tmpevalinfo.MinimumPrice = min
            End If
            If mode > 0 Then
                tmpevalinfo.ReoccuringPrice = mode
            End If
            If avg > 0 Then
                tmpevalinfo.avgprice = avg
            End If
            If max > 0 Then
                tmpevalinfo.MaximumPrice = max
            End If
            If standarddev > 0 Then  'And standarddev < 1 Then
                tmpevalinfo.avgdeviation = standarddev
            End If
            min = 0
            max = 0
            mode = 0
            avg = 0
            tmpevalinfo.currentItem = tmpiteminfo2.Item(f).name
            tmpevalinfo.evaluationcondition = tmpiteminfo2.Item(f).itemcondition
            'MsgBox(tmpiteminfo2.Item(f).itemcondition)
            tmpevalinfo.evaluated = True
            itemevalresultarr.Add(tmpevalinfo)

        Next f
        'Dim iteminfoc As New ItemInformation
        'For s = 0 To tmpiteminfo.Count - 1
        '    iteminfoc.itemsevaluate.Add(tmpiteminfo.Item(s).name)
        '    'add other item information and change to iteminfo type once I get information to display correctly on findquote form
        'Next s
        Dim itemval As New ItemValuation
        Dim tmpevalsource As New EvaluationSource
        Using uie As New DataAccess.SQLiteDb
            For h = 0 To tmpiteminfo.Count - 1
                tmpevalsource.EvaluationSourceName = tmpiteminfo.Item(h).evaluationSource
                tmpevalsource.EvaluationWebAddress = (From EvalWeb In uie.ValuationSources Where EvalWeb.ValuationSourceName = tmpevalsource.EvaluationSourceName Select EvalWeb).ToString
                Await itemval.archiveitem(tmpiteminfo.Item(h).name, tmpiteminfo.Item(h).name, tmpevalsource)
            Next
        End Using
        'delete all the evaluated items from the database for next evaluation

        Await ClearEvalTable()
        'return regular results
        Return itemevalresultarr
    Else
        'return automated evaluation results
        'results are archived as they are retrieved so delete data from the table below
        'delete all the evaluated items from the database for next evaluation
        Return autoitem
    End If

End Function

Here's the inner function's signature:

Private Async Function GrabFromOnlineSource(itemname As String, WebsiteSource As String, progress As IProgress(Of Integer)) As Task(Of List(Of EvaluationInfo))

Here's what I have tried so far (updated):

first try:

  tmpevalitem = Await GrabFromOnlineSource(item.Item(i).name, item.Item(i).evaluationSource, New Progress(Of Integer)(Function(value) InlineAssignHelper(totalprogress, value))). 'replace totalprogress with the a integer I mentioned in the comments.
                  tmpevalitem.Wait()

second try:

   tmpevalitem = GrabFromOnlineSource(item.Item(i).name, item.Item(i).evaluationSource, New Progress(Of IProgress(Of Integer))(Function(value) InlineAssignHelper(evalprogress, value)))
                        tmpevalitem.Wait()

The above code though ends up in an error about data type progress not being correct. I really need to pass my progress from GrabFromOnlineSource to the evalprogress variable or otherwise I will have differing values sent to the main program.

Note: I technically could have GrabFromOnlineSource called for each evaluation source used (4 total possible as of this moment). In addition, each evaluation source sent to GrabFromOnlineSource has its own progress reported counted toward the current evaluation source (i.e. ebay has its own progress, amazon its own, etc).

The EvaluationInfo and ItemInfo Structures:

     Public Structure EvaluationInfo
        Dim ReoccuringPrice As Double
        Dim MinimumPrice As Double
        Dim MaximumPrice As Double
        Dim avgprice As Double
        Dim avgdeviation As Double
        Dim currentItem As String
        Dim valuationsourceid As Integer
        Dim evaluationcondition As ItemCondition 'i.e. used,new,broken enum
        Dim evaluated As Boolean
    End Structure



 Public Structure ItemInfo
            Public Property name As String
            Public Property price As Double
            Public Property damaged As String
            Public Property aquisitiondate As Date
            Public Property evaluationSource As String
            Public Property itemcondition As ItemCondition
            Public Property itempicture As Byte()
            Public Property itemid As Integer

        End Structure

More Information:

One thing I forgot to mention progress reporting does work with task.run from evaluateitem function but I prefer to modularize my code a bit to prevent one action from taking too much space and that's why I have not moved the code from GrabFromOnlineSource into evaluateitem.

jeffery
  • 322
  • 4
  • 19
  • Please provide a [mcve]. – Timo Oct 12 '17 at 02:07
  • I forgot to pass my return value structures. I'll post code in for an edit for the main one later today. However, as I will be publishing this later I CANNOT reveal inner functions (GrabFromOnlineSource) code. Sorry. – jeffery Oct 13 '17 at 13:41
  • I tried to simplify your code and want to reproduce your issue, unfortunately, I have not reproduced it. Have you tried to remove this line `tmpevalitem = GrabFromOnlineSource(item.Item(i).name, item.Item(i).evaluationSource, New Progress(Of Integer)(Function(value) InlineAssignHelper(a, value)))` to see if you will still face this issue? – Timo Oct 17 '17 at 07:29
  • It's not clear what your evalprogress does since we only have the interface and don't know what the concrete implementation of it is. But assuming the main function is being run as a background task, the evalfunction needs to use Invoke or BeginInvoke in order for the progress report to happen on the UI thread. – dwilliss Oct 17 '17 at 13:39
  • @Skyblue, the progress from the main function works which I have verified through manual testing (over and over again to double check). However, I can transfer the integer value in this line `New Progress(Of Integer)(Function(value) InlineAssignHelper(a, value))` to the integer variable named _a_. However, I need to transfer the value through evalprogress for this to work smoothly but can take any code remakes or possible implmentations you have that pass progress up the chain. I tried using the integer variable _a_ in the main program in a loop by making it shared but that was a disaster. – jeffery Oct 17 '17 at 20:22
  • @dwilliss, default implementation of IProgress from Microsoft UWP API on build 10586 in visual studio 2015 (its implementation is very simple). – jeffery Oct 17 '17 at 20:35
  • Isn't `GrabFromOnlineSource` async Task? Why are you blocking the UI thread with `tmpevalitem.Wait()`? If you got the progress handler from UI (I suppose), then surely when you have blocked the thread, the progress also won't work. Have you tried to *await GrabFromOnlineSource(...)*? – Romasz Oct 17 '17 at 20:43
  • @Romasz, look at the code try for GrabFromOnlineSource again. where I use the integer variable totalprogress but replace that with a and you see the value goes nowhere but to the integer and not up the chain. I could use await but I really need that function to finish or else the rest is just bread without the meat. – jeffery Oct 17 '17 at 20:57
  • @Romasz, I tried to await the inner function and main function both but that does not pass progress back either. However, it does speed up passing progress back from the main function but delayed and UI is locking up still for a moment. It does work faster though but not by much. If I could understand when the inner function is called maybe I could switch to a different progress bar and then switch back but I prefer not to do that. – jeffery Oct 17 '17 at 21:08
  • @Romasz, sorry for using your tag again but I made a typo because it does not have a delay always in passing progress back from the main function but does lock up but for less then 30 seconds usually in the UI except for progress updates. – jeffery Oct 17 '17 at 21:15

2 Answers2

2

have u try declarated one variable in function GrabFromOnlineSource as byref and pass to this method. after u modality in GrabFromOnlineSource u can observe this variable from main function or anny other place.

Sorry i should check that

Method nr.2

So why u dont wrap this process in class, something like that if u wanna progress in 2 task, u can always observe this object or add event for progress tracking

    Public Sub SomeMethod()
    Dim Wrap1 As New WrapConteiner
    Dim Wrap2 As New WrapConteiner
    Wrap2.Sleep = 200
    Dim Tasks(0 To 1) As Task
    Tasks(0) = New Task(AddressOf Wrap1.AwaitedProcessAsync)
    Tasks(1) = New Task(AddressOf Wrap2.AwaitedProcessAsync)
    Tasks(0).Start()
    Tasks(1).Start()
    Do While (Tasks(0).Status = TaskStatus.Running Or Tasks(1).Status = TaskStatus.Running Or Tasks(0).Status = TaskStatus.WaitingToRun Or Tasks(1).Status = TaskStatus.WaitingToRun)
        Task.WaitAll(Tasks, 10)
        Debug.Print("Wrap1: " & Wrap1.Progress)
        Debug.Print("Wrap2: " & Wrap2.Progress)
    Loop
    Debug.Print("B")
End Sub

Public Class WrapConteiner
    Property Progress As Integer
    Property Sleep As Integer = 100
    Public Sub AwaitedProcessAsync()
        Dim x As Integer
        For x = 1 To 100
            Me.Progress = x
            Threading.Thread.Sleep(Sleep)
        Next
    End Sub
End Class
  • Sorry, I cannot use the variable as byref either because visual studio throws a simple error that I cannot use a byref parameter in an async function. I would have done that if it were that easy. The progress works from the main function but not the inner or secondary function. – jeffery Oct 18 '17 at 20:29
  • Loop does not work because it completes the function then performs the loop which it skips. – jeffery Oct 22 '17 at 19:45
0

Sorry for the delay in answering my own question but here goes (I am not perfect at formatting code so an editor or admin can pretty it up):

This is the class test code I used separately from my projects dll to test this was working (aka I created a new project to test my theory):

Imports Windows.ApplicationModel.Core
Imports Windows.UI.Core

Public Class Progresstestclass
    Private Shared Property totalprogress As Integer
    Public Shared Async Function MainFunction(progress As IProgress(Of Integer)) As Task(Of Boolean)

        totalprogress = 5
        progress.Report(totalprogress)
        Dim a As New String("")
        Do While totalprogress > 0 And totalprogress < 70 And a <> "Success"
            Await InnerFunction(New Progress(Of Integer)(Function(value) InlineAssignHelper(totalprogress, value)))
            progress.Report(totalprogress - 5)
            Await System.Threading.Tasks.Task.Delay(1000)
            progress.Report(totalprogress)

        Loop
        Return True
    End Function
    Private Shared Async Function InnerFunction(progress2 As IProgress(Of Integer)) As Task(Of String)
    progress2.report(30)
            Await System.Threading.Tasks.Task.Delay(1000)
            progress2.Report(40)
          Await System.Threading.Tasks.Task.Delay(1000) 

        Return "Success"
    End Function
    Public Shared Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
        target = value
        Return value
    End Function

End Class

Another thing that changed inside my main function was my while loop was a little wrong. I needed put in delays in-between passing progress reports back or else it did not pass it back or showed it at 100% at the end making my progress report useless. Anyone can still comment on best how to do this. Its a crazy thing that forgoting delays can mess up a program!

Everything else is pretty much the same. Except I put delays as above to have it stop passing progress long enough for the natural eye to see the progress happening.

jeffery
  • 322
  • 4
  • 19
  • Note: I know I did not need the second progress.report in the main function but wanted to see it slowly progress through the items in-case I had a lot and I will remove it in my main program. – jeffery Dec 07 '17 at 22:26