New version of Quick Objects is now available! Download V5 today or read further!


Search for...   
Register Login
View Cart  View Cart Checkout  Checkout
Topic Topic:
Advice needed on handling updating objects while in a loop of objectcollection items
Posted On: 12/3/2008 6:39 PM
Posted By: Mike Hamilton


WHEW! that subject was a mouthful, or should I say a textboxful! LOL

anyways here is the situation, hopefully I can explain it clearly so I can get advice on how I should handle it.

we have an object base on a table that is a queue for items that need to be sent. The problem is that this can be a long running process to compile and send these items so we want to update the table to show it is being processed so that if another run of the processing system starts this item would not be found in the find run to get items to be processed.

    Private Sub ProcessFolderSendQueue()  
        ' get reference to FolderSend  
        Using ReadyItems As New BizLibrary.FolderSend()  
            ' get the items ready to send (status=2)  
            ReadyItems.ObjectMode = Akal.QuickObjects.ObjectBase.ObjectModes.Search  
            ReadyItems.Status.Parse(2)  
 
            ' set sort order  
            ReadyItems.Send_Method.Set(Akal.QuickObjects.ObjectBase.SortTypes.Ascending, 1)  
            ReadyItems.Date_Submitted.Set(Akal.QuickObjects.ObjectBase.SortTypes.Ascending, 2)  
 
            ' do find  
            ReadyItems.Find()  
 
            ' loop through any found  
            For Each ReadyItem As BizLibrary.FolderSend In ReadyItems.List  
                ' set as processing  
                ReadyItem.MarkProcessingStarted()  
 
                ' do processing  
 
                ' set as sent  
                ReadyItem.MarkProcessingComplete()  
            Next 
        End Using  
    End Sub 
 

what I am wondering is how I should handle the MarkProcessingStarted()  and MarkProcessingComplete() methods on the object.

what checks should I do in those methods? should I check the object is loaded using "Me.IsLoaded" first? should I just do this
Public Sub MarkProcessingStarted()  
    With Me 
        .ObjectMode = ObjectModes.Save  
        .Status.Value = 3     ' 3= processing  
        .Update()  
    End With 
End Sub 
 
 

or should I create a new object and run update on that object?

Is it reccomended (or safe) to go updating the objects while they are being processed as they are in the loop of the item colelction?

Thanks!

Mike



Replies Posted On / By
  Mike,

This is an interesting question. To start of, it is safe to update object while looping through the item collection.  In fact that sometimes is more desirable than creating a new instance to perform an update. If you create a new instance to do the update you will not have the capability perform a concurrency check out of the box, as in you can still do it but you will need to also assign/load the original values as well.  If you use the same instance and you have the ConcurrencyMode = DetectChanges then the concurrency check is automatic.

From what I understand, you have a high number of objects that basically need to be processed. Once the processing starts on an Object you don't want another process/thread to process the same object. To solve this in addition to using the MarkProcessingStarted and MarkProcessingComplete methods I would add another method that will perform a quick check before starting the processing to make sure that no other process has begun processing.  This however will cause extra queries going to the server and will add to the amount of processing time.  This may not be an issue as you can reduce the query to pretty much "SELECT Status FROM FolderSend WHERE FolderSendID = X" but if you did the check for every record and you had 100k records you will be hitting your DB 100k times to perform a check. 

Another solution would be to do something like this:

    Private Sub ProcessOrders() 
        Using order As Orders = New Orders() 
            order.Status.Value = 2 ' Ready To Send 
            order.Status.UseInSearch = True 
 
            order.MaxRecords = 10  ' You could also limit the number of records you will be processing.
            order.Find() 
 
            order.Status.Value = 3 ' Processing 
            order.Status.UseInSave = True 
 
            order.OrderID.SearchMode = SearchModes.List 
            ' The following line will get a comma seperated list of OrderID values that have already been loaded. 
            order.OrderID.List.CustomValue = order.GetDelimetedValue(order.ResultTable.Rows, order.OrderID.Name, ","
 
            ' After this statement only OrderID's UseInSearch property is set to True 
            order.SetSearchFields(True, order.OrderID) 
 
            If order.BulkUpdate() Then 
                ' Now that marked multiple order objects (that we fetched earlier) as being processed, we can switch the SearchMode of OrderID to Value 
                ' that way next time OrderID is used in Search criteria its single value will be used rather than the list of comma seperated values. 
                order.OrderID.SearchMode = SearchModes.Value 
 
                ' Now start processing the orders 
 
                For Each o As Orders In order.List 
                    ' Do something with the Order 
 
                    o.Status.Value = 4 ' Complete 
                    o.Status.UseInSave = True 
 
                    If o.Update() Then 
                        'MessageBox.Show("Order Processed for " + o.OrderID.ToString()) 
                    End If 
                Next 
 
                ' Another choice would be to run another BulkUpdate call and set all the processed rows to Complete in one SQL statement. 
 
            End If 
        End Using 
    End Sub 
 

This choice of using BulkUpdate can significantly improve performance and reduce the load on your server as well.

As far as "what checks should I do in those methods? should I check the object is loaded using "Me.IsLoaded" first? should I just do this" is concerned:
The method should primarily make sure that the Primary Key has a value or the Update will fail. Then could be done as below:

If Not .MyID.IsNull Then 
   .Status.Value = 3 
   .Update() ' well Ideally you would check if the Update worked or not 
End If 


I hope I didn't miss anything, but if I did or something is unclear or you need further clarification do let me know.

Thanks,
Ish
 
12/4/2008 9:14:59 AM
Ish Singh
  Ish,

Thanks for the advice.

I had forgot about the check of the item to make sure it was not grabbed by another run of the processing method. I dont forsee the numbers being that huge so the check of status before processing will not be much. But I will think about doing the bulk updates as you suggested too.

I did go ahead and write the "Mark" methods on the object while waiting for response and did include the check of the PK as you listed, so I did get that right. LOL

Mike
 
12/4/2008 5:09:44 PM
Mike Hamilton
  Mike,

One more trick and word of caution. When you are looping through items and you want to run another query/select against the same object, I highly recommend using the AddResultToDataSet = False.

Here is an example:
    Public Function IsProcessed() As Boolean 
        Me.AddResultsToDataSet = False 
        Me.UseAllFieldsForDisplay(False
        Me.OrderProcessed.Visible = True 
        If Me.Load() And Me.OrderProcessed.Value Then 
            Return True 
        End If 
        Return False 
    End Function 
 
 

Ofcourse if your field can contain null values you can also do a check for .IsNull before checking the value :)

Thanks,
Ish

 
12/4/2008 10:26:08 PM
Ish Singh