Asynchrous rule not updating UI on first call - breaking list

Asynchrous rule not updating UI on first call - breaking list

Old forum URL: forums.lhotka.net/forums/t/10173.aspx


zman88a posted on Thursday, March 17, 2011

I’ve been using CSLA for a while now but am now just starting to build Silverlight applications using it.  I am having a problem with asynchronous rule methods that update the UI.

I am using CSLA 4.1 and have the following objects:

BannedCarrier – Child Business Base Object
BannedCarriers – Editable Business List Base

The Banned Carrier Object has 3 properties:  Code, Name, and Level

The code and level properties are editable and the name property is read only and displays the name value of the code property based on a lookup from a reference list that is cached on the first fetch.   During fetch the names are loaded synchronously but I have an asynchronous business rule that populates it when a code is changed or a new carrier ban is added. 

The problem I am running into is that when that asynchronous rule is called the first time it does not update the UI and for some reason makes my list un-savable.  If I change the code property in another row the rule fires and the name property is updated in the UI for that row but my list still remains un-savable.  If I try to go back and update the first row again there is still no change.  If I do a CancelEdit and try again that same child continues to have the problem.  It seems as if that first object gets stuck somehow for the life of the list.  When I remove the rule I can add and update without issue.

Here is part of the code for my child object

 

Public Shared ReadOnly CarrierCodeProperty As PropertyInfo(Of String) = RegisterProperty(Of String)(Function(c) c.CarrierCode, "Carrier Code")

Public Property CarrierCode() As String

        Get

            Return GetProperty(CarrierCodeProperty)

        End Get

        Set(ByVal value As String)

            SetProperty(CarrierCodeProperty, value)

        End Set

End Property

 

Public Shared ReadOnly CarrierNameProperty As PropertyInfo(Of String) = RegisterProperty(Of String)(Function(c) c.CarrierName)

Public ReadOnly Property CarrierName() As String

        Get

            Return ReadProperty(CarrierNameProperty)

        End Get

End Property

 

Protected Overrides Sub AddBusinessRules()

     BusinessRules.AddRule(New Rules.CommonRules.Required(CarrierCodeProperty))
   
BusinessRules.AddRule(New Rules.CommonRules.MaxLength(CarrierCodeProperty, 2))
  
BusinessRules.AddRule(New SetCarrierName(CarrierCodeProperty, CarrierNameProperty))
End Sub

 

 

Private Class SetCarrierName

        Inherits Rules.BusinessRule

 

        Public Property CarrierName As IPropertyInfo

 

        Public Sub New(ByVal carrierCodeProperty As IPropertyInfo,

                       ByVal carrierNameProperty As IPropertyInfo)

            MyBase.New(carrierCodeProperty)

            CarrierName = carrierNameProperty

            InputProperties = New List(Of IPropertyInfo) From {PrimaryProperty}

            AffectedProperties.Add(CarrierName)

            IsAsync = True

        End Sub

 

        Protected Overrides Sub Execute(ByVal context As Csla.Rules.RuleContext)

            Dim code = CType(context.InputPropertyValues(PrimaryProperty), String)

            Dim name As String = String.Empty

            Reference.CarrierList.GetList(Sub(sender As Object, e As DataPortalResult(Of Reference.CarrierList))

                                                      name = e.Object.GetName(code)

                                                      context.AddOutValue(CarrierNameProperty, name)

                                                      context.Complete()

                                                  End Sub)

 

        End Sub

 

    End Class

 

 

Any help would be greatly appreciated. 

Thank you in advance.

RockfordLhotka replied on Thursday, March 17, 2011

Are you calling CheckRules in your DataPortal_Fetch?

zman88a replied on Thursday, March 17, 2011

Thanks for the quick reply!

No I'm not calling CheckRules in fetch, here is my fetch code.

For the list just your basic L2S call

Private Overloads Sub DataPortal_Fetch()
   Me.RaiseListChangedEvents = False
   Using ctx = ContextManager(Of Dal.DBDataContext).GetManager(Dal.Database.DBConnection)
      Dim data = From c In ctx.DataContext.BannedCarriers
                
Order By c.Carrier Descending
                
Select (BannedCarrier.GetBannedCarrier(c))
  
      Me
.AddRange(data)
   End Using
   Me.RaiseListChangedEvents = True
End Sub

 And then in the child object just loading in the individual values

 Private Sub Child_Fetch(ByVal data As Dal.BannedCarrier)
   LoadProperty(IdProperty, data.Id)
   LoadProperty(CarrierCodeProperty, data.Carrier)
   LoadProperty(LevelProperty, data.Level)
   LoadProperty(CarrierNameProperty, Reference.
CarrierList.GetList.GetName(data.Carrier))
End Sub

zman88a replied on Thursday, March 17, 2011

I think I figured out at least part of the problem, still not sure why it is happening though or how to fix it.  After reading another post regarding not being able to save an object that is busy I added a check to display the IsBusy and IsSelfBusy properties for all the child objects in the collection.  The first object that runs that rule gets it's IsBusy and IsSelfBusy properties to True and they get stuck with that status.  I changed subsequent children that will fire that rule and the UI updates like it should and their busy status' revert back to False immediately, but that initial obect stays "Busy" until I close the view.  Since that one object is always busy I believe that is why UI will not update and ceratinly the reason my list is never saveable.  It still perplexes me as to why that first edited object gets stuck and all the others do not.

ajj3085 replied on Thursday, March 17, 2011

It sounds like your async rule is not ever being completed. 

JonnyBee replied on Sunday, March 20, 2011

Is one of your objects a New object (and is base.DataPortal_Create) called.

base.DataPortal_Create will call BusinessRules.CheckRules and may make the object busy and return it to the client if running in an N-tier configuration.

zman88a replied on Monday, March 21, 2011

No, the hangup occurs on the first object that is edited.  It will happen whether a "New" item has been added to the collection or just the current item list is being updated.  At first I had also thought that the rule was not completing, but that does not seem to be the case since it works as designed for any subsequent objects edited in the same way.  

JonnyBee replied on Monday, March 21, 2011

What if you get an Exception in the data access / tranport?

In the async callback - the first thing your code should check for is if e.Error <> Null then context.AddErrorResult with exception message.

zman88a replied on Tuesday, March 22, 2011

I figured it out.  It was an error in my lookup list class.  I'm caching the results and forgot to return the callback in the fetch completed handler when it actually needs to go get data.  So the rule was waiting for a callback that never came back the first time but since the list did get cached it was coming back for the second or third objects.  But I did add the error handling to the rule as well, thanks for the tip.

Public Shared Sub GetList(ByVal callback As EventHandler(Of DataPortalResult(Of CarrierList)))

If
mList Is Nothing Then
   Dim dp As New DataPortal(Of CarrierList)
   AddHandler dp.FetchCompleted, Sub(sender As Object, e As DataPortalResult(Of CarrierList))
                                     
If e.Error Is Nothing Then
                                       
mList = e.Object
                                        callback(sender,e
)
                                     
End If
                                 End Sub
   dp.BeginFetch()
Else
  
callback(Nothing, New DataPortalResult(Of CarrierList)(mList, Nothing, Nothing))
End If

End Sub 

 

JonnyBee replied on Tuesday, March 22, 2011

Hey,

You must always call the callback event handler in Fetch completed. Otherwise you'll end up in that same situation if Fetch failed.

And you would maybe also add logic so that you will not start async fetch more than one times - remeber this is called from an Async rule and may be triggered many times until the first FetchCompleted is called.

So - as a minimum like this:

Public Shared Sub GetList(ByVal callback As EventHandler(Of DataPortalResult(Of CarrierList)))

If
mList Is Nothing Then
   Dim dp As New DataPortal(Of CarrierList)
   AddHandler dp.FetchCompleted, Sub(sender As Object, e As DataPortalResult(Of CarrierList))
                                     
If e.Error Is Nothing Then
                                       
mList = e.Object
                                     
End If
                                     callback(sender,e)
                                 End
Sub
   dp.BeginFetch()
Else
  
callback(Nothing, New DataPortalResult(Of CarrierList)(mList, Nothing, Nothing))
End If

End Sub

 

 

zman88a replied on Wednesday, March 23, 2011

Great, I will make that update.  Thanks for all your help!

Copyright (c) Marimer LLC