Incorrect IsValid value on BrokenRulesCollection.ListChanged event handler

Incorrect IsValid value on BrokenRulesCollection.ListChanged event handler

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


amselem posted on Wednesday, January 10, 2007

Hi all,

I've a subclass of BusinessBase(Of T) wich has as sub listening for the ValidationRules.GetBrokenRules.ListChanged event.

If the argument e.ListChangedType equals ComponentModel.ListChangedType.ItemAdded then I must check the IsValid value. However this value is not correct inside this event handler.

I've tracked the problem to this method in BrokenRulesCollection.vb:

Sub Add(ByVal rule As IRuleMethod)
...
Add(item)
IncrementCount(item)
...

IsValid depends on mErrorCount and this variable gets incremented on IncrementCount. But the ItemAdded event gets fired before this increment so the IsValid value is not correct within the event handler context.

The Remove Sub is working because it's decrementing this counter before removing the item.

So I think these two lines should be swapped, but I don't know if that could break existing code. What do you think?

Jacobo

 

ajj3085 replied on Wednesday, January 10, 2007

I wouldn't think that would be a problem, but I have to ask, why are you listening for events to the BrokenRulesCollection?  I don't think that's really been done before.

amselem replied on Wednesday, January 10, 2007

I'm using ActiveObjects 2.0 and the ActiveBusinessBase class listens to that event so it can fire an observable IsValid event. This is the code :

Private Sub BrokenRulesListChangedHandler(ByVal sender As Object, ByVal e As System.ComponentModel.ListChangedEventArgs)

Select Case e.ListChangedType

Case ComponentModel.ListChangedType.ItemAdded, ComponentModel.ListChangedType.ItemDeleted

Notify(BusinessEvents.IsValid, Me.IsValid)

 

ajj3085 replied on Wednesday, January 10, 2007

Ahh, ok.  Well if you want that to work you'll have to make the change and see how it works.  It didn't look like to me swapping the statements would have an effect, but at the same time it seems ActiveObjects is depending on implementation details of Csla... hmm..

amselem replied on Wednesday, January 10, 2007

Well I already have changed it and have my code working without problems. This post is more a suggestion to Rocky to swap those two statements and fix this tiny bug.

This change would also match the Remove implementation  (decrementing then removing item) as I said.

As for the ActiveObjects dependency on CSLA I don't see that it's doing anything illegal, it's just subclassing businessbase and listening an event from the protected variable ValidationRules. I could have another scenario without AO at all, and still reproduce the issue.
(BTW I think this problem was introduced with CSLA 2.1, because since then IsValid is based on the mErrorCount variable rather than the .Count method)

Regards

cmellon replied on Wednesday, January 10, 2007

I think the issue with swapping them arround is that is the Add fails, the count will then be incorrect.  Whereas at the moment if the add fails then the counter wont be incremented? I'm not 100% sure about this, but its the first thing I noticed when looking at the code your posted.

Thinking about that, would this mean that if the remove failed the count would also be incorrect? could the remove ever fail? 

Just a thought

Craig

amselem replied on Wednesday, January 10, 2007

Hi Craig,

You're correct, if the Add or Remove methods fail the counter would get unsynchronized. I don't know if this could ever happen but I think that is very improbable because we're talking about the BrokenRulesCollection class wich is wrapped inside ValidationRules. So the Add/Remove code is a little more 'protected' from the user because we normally don't interact directly with BrokenRulesCollection ...

Anyway I still think that those lines should be swapped in the Add sub, at least to expose the same behavior in both methods

ajj3085 replied on Thursday, January 11, 2007

amselem:
You're correct, if the Add or Remove methods fail the counter would get unsynchronized. I don't know if this could ever happen ...


Well I think if the Add or Remove fails you would get an exception, so I'm not sure at that point if the counters are what we should be worrying about.

Is there a way for the Add / Remove to fail without raising an exception?

ajj3085 replied on Thursday, January 11, 2007

amselem:
Well I already have changed it and have my code working without problems. This post is more a suggestion to Rocky to swap those two statements and fix this tiny bug.


Assuming its even a bug.  There could be a reason the code is executed in this order.

amselem:
This change would also match the Remove implementation  (decrementing then removing item) as I said.

Again, there could be a reason for the order in which the statements are executed.  I'm not saying for sure there is, we don't know...

amselem:
As for the ActiveObjects dependency on CSLA I don't see that it's doing anything illegal, it's just subclassing businessbase and listening an event from the protected variable ValidationRules. I could have another scenario without AO at all, and still reproduce the issue.
(BTW I think this problem was introduced with CSLA 2.1, because since then IsValid is based on the mErrorCount variable rather than the .Count method)

Its depending on the fact that the ListChanged event is only called after the counters are updated.  Now, that's probably a valid assumption, and unless there's a good reason the code should probably be changed, but it still seems like AO is depending on how the Add method is implemented.

JohnB replied on Tuesday, February 12, 2008

Hi Amselem,

I was just reading your post and was wondering if you're still using this technique of listening to the broken rules ListChangedEvent for your BusinessBase subclass? If so, have you had any issues with it?

I would also be curious to see how your subclass of BusinessBase looks.

Thanks,
John

amselem replied on Friday, February 15, 2008

Hi John

Yes, I'm still using this technique and didn't have any issues after this post. This is the relevant code from my BusinessBase subclass, just don't forget to add/remove the handler when de/serializing the object :


   Protected Sub New()
      MyBase.new()
      Me.AddHandlers()
    End Sub

   Protected Overrides Sub OnDeserialized(ByVal context As StreamingContext)
      MyBase.OnDeserialized(context)
      Me.AddHandlers()
    End Sub
 
    <OnSerialized()> _
    Protected Sub OnSerialized(ByVal context As StreamingContext)
      Me.RemoveHandlers()
    End Sub

    Private Sub AddHandlers()
      ' Handle events raised by our BrokenRules
      AddHandler ValidationRules.GetBrokenRules.ListChanged, AddressOf BrokenRulesListChangedHandler
    End Sub

    Private Sub RemoveHandlers()
      ' Handle events raised by our BrokenRules
      RemoveHandler ValidationRules.GetBrokenRules.ListChanged, AddressOf BrokenRulesListChangedHandler
    End Sub


    Private mOldIsValidState As Boolean = False
    Private mIsValidStateInitialized As Boolean = False

    Private Sub BrokenRulesListChangedHandler(ByVal sender As Object, ByVal e As System.ComponentModel.ListChangedEventArgs)
      Select Case e.ListChangedType

        Case ComponentModel.ListChangedType.ItemAdded, ComponentModel.ListChangedType.ItemDeleted

          Dim newIsValidState As Boolean = Me.IsValid

          ' Check if our IsValid state has changed (only our own, not of child objects too)
          If (Not mIsValidStateInitialized) OrElse (mOldIsValidState <> newIsValidState) Then
              mIsValidStateInitialized = True
              mOldIsValidState = newIsValidState
              ' fire the IsValid event since our IsValid state has changed
              Notify(BusinessEvents.IsValid, newIsValidState)
          End If
        End Select

    End Sub

 

HTH
amselem

JohnB replied on Friday, February 15, 2008

Thanks. It will come in handy. We actually created our own BrokenRulesCollection, since it is non-inheritable, so that we can property retrieve broken rules for a parent and all of its children. We will also use it for collections too.

The reason for this is that we have a message panel that we use to display all broken rules to the user. Very similar to the Error List panel in Visual Studio.

Thanks again,
John

RockfordLhotka replied on Thursday, January 11, 2007

I don't see where this change is a problem, and it brings the code more in line with the remove functionality. I did the change and it broke no unit tests, so I'll leave it in there for 2.1.2.

Copyright (c) Marimer LLC