OnListChanged - PropertyDescriptor - Remoting vs. Non-Remoting

OnListChanged - PropertyDescriptor - Remoting vs. Non-Remoting

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


skagen00 posted on Tuesday, August 01, 2006

I can't see that this has come up...

I was programming with remoting turned off, and noticed when I was overriding OnListChanged that I could determine which property was being changed.

This came in handy for me as I wanted to do some special checking on members of the collection, then setting an internal setter on the child for a property that would say that a child has an conflict with other children that makes it invalid. (All the conflicting children would get this property set to true, and by tying a rule to it, I can display an (!) wherever I want to as I tie my rule to this property).

This conflict has to do with a date range that cannot overlap across child objects. I used an efficient algorithm but still I really only want to call it when necessary. So when one of the five possible fields change that could change the validity of the children, I want to do this special logic. Otherwise, I'd rather not do it.

To determine if I did this extra logic, I found I could look at e.PropertyDescriptor.Name in the ListChangedEventArgs. Worked great. So I turn on remoting, and nothing is working...

In BusinessListBase, I notice this code executes when remoting is turned on:

private void Child_PropertyChanged(object sender, PropertyChangedEventArgs e)

{

   for (int index = 0; index < Count; index++)

   {

      if (ReferenceEquals(this[index], sender))

      {

         OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));

         return;

      }

   }

}

This overload to OnListChanged contains a ListChangedEventArgs which does not include the property name. While PropertyChangedEventArgs contains the property name, it was not immediately evident how one would get a PropertyDescriptor to pass in the other overload of ListChangedEventArgs.

As a consequence, OnListChanged doesn't have access to the property being changed, and I had to remove this selective processing from the collection.

I appreciate that I could have read further into the 2.0 book as maybe it explains it (I read the first book but haven't read the 2.0 book in depth).

But I guess I have two questions - why is there a difference between remoting and non-remoting for this problem, and is there an easy workaround to be able to have access to the changing property in the OnListChanged event when using remoting?

Thanks!

(using 2.0.3)

Brian Criswell replied on Tuesday, August 01, 2006

Hmmm, BindingList<T> might have been doing some magic with link PropertyChanged events to ListChanged events.

As for covering it yourself, you could try:

private void Child_PropertyChanged(object sender, PropertyChangedEventArgs e)

{

   for (int index = 0; index < Count; index++)

   {

      if (ReferenceEquals(this[index], sender))

      {

          PropertyDescriptor prop = TypeDescriptor.GetProperties(sender)[e.PropertyName];

         OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index, prop));

         return;

      }

   }

}


skagen00 replied on Tuesday, August 01, 2006

Thanks Brian for the quick response.

This indeed did the trick. I hesitate to make the change to the framework as I'd like to stay true, so I will likely refrain from making the change to the framework.

Though it seems to me that if what is done isn't a large hit on performance that it would provide a certain consistency between the behavior when using remoting vs. non-remoting.

Good to know I have an option for this if I need it. Thanks again.

RockfordLhotka replied on Tuesday, August 01, 2006

This sounds like a bug. I wasn't aware that there was a difference in the args parameter.

BindingList<T> automatically sets up a handler for PropertyChanged when a child object is added. Unfortunately they don't automatically reset that handler when the collection is deserialized, and that's why I wrote the code you are talking about.

Unfortunately, it would appear that I am not entirely duplicating their behavior in terms of how I raise the ListChanged event...

Based on the property name, it is possible to get a PropertyDesriptor, but it is a relatively expensive operation, so they must do some caching of the descriptors (presumably by type, because a collection could have different types of child object). Apparently I'll need to do that same thing in this event handling code.

skagen00 replied on Thursday, September 21, 2006

I just wanted to make a note that this appears to have been remedied in 2.1, thank you!

(I didn't see it in the change log, but it sure looks like it's working now so I just have to assume you made a change).

JoeFallon1 replied on Thursday, September 21, 2006

Here is the 2.1 code:

Private Sub Child_PropertyChanged(ByVal sender As Object, _

ByVal e As System.ComponentModel.PropertyChangedEventArgs)

  For index As Integer = 0 To Count - 1

    If ReferenceEquals(Me(index), sender) Then

      OnListChanged(New System.ComponentModel.ListChangedEventArgs( _

      ComponentModel.ListChangedType.ItemChanged, index, GetPropertyDescriptor(e.PropertyName)))

      Exit For

    End If

  Next

End Sub

Joe

skagen00 replied on Thursday, September 21, 2006

The code there is in EditableRootListBase - at least in my 2.1 that exact code doesn't exist in BusinessListBase and I don't think it's actually relevant anymore.

I notice that the code isn't being even run (Child_PropertyChanged) - so it must be running through the automatic handler - Rocky mentioned that when it was being deserialized, the handler wasn't been reestablished correctly.

I bet Rocky's changes to the deserialization process remedied this issue.

 

JoeFallon1 replied on Thursday, September 21, 2006

Public MustInherit Class BusinessListBase( _

Of T As BusinessListBase(Of T, C), C As Core.IEditableBusinessObject)

Inherits Core.ExtendedBindingList(Of C)

Implements Core.IEditableCollection

Implements ICloneable

Implements ISavable

Implements IParent

 

...

 

Private Sub Child_PropertyChanged(ByVal sender As Object, _

ByVal e As System.ComponentModel.PropertyChangedEventArgs)

 

...

I downloaded 2.1 on Sep 14.

Joe

 

skagen00 replied on Thursday, September 21, 2006

I'm not sure, the current version downloadable (not counting CVS, for BusinessListBase) is:


    private void Child_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      for (int index = 0; index < Count; index++)
      {
        if (ReferenceEquals(this[index], sender))
        {
          OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));
          return;
        }
      }
    }

As far as I can tell, there are no changes to that. It passes along what row changes but not which property. This does not pass in what you'd need to get the name of the property that changes. (There are a few different overloads.)

Regardless, I'm not so sure it's an issue anymore. Does your BusinessListBase look different than what I have? You using the CVS version?

skagen00 replied on Thursday, September 21, 2006

Ah, the VB version is different than the C# version. 

I'd be interested if Rocky would comment on if the deserialization changes makes this a non-issue. Because the C# version which does not have the code you are seeing, seems to work now (the method doesn't seem to get hit even).

RockfordLhotka replied on Thursday, September 21, 2006

Huh. On two counts: huh.

First, I obviously was playing with a solution and checked it in - which wasn't the plan. I'll have to clean that up.

Second, I really don't know what I might have done that would make it start working. Are you sure your test is valid? Wink [;)]

jwooley replied on Thursday, February 15, 2007

I ran into this issue as well in a remoting environment with 2.1.2. The previously mentioned fixes worked for me. I checked the VB and C# code and they both appear to be missing the prop parameter of the OnListChanged call. Below is the code I used in VB (I think Joe leveraged a GetPropertyDescriptor method which I didn't find in the framework at this point).

Private Sub Child_PropertyChanged(ByVal sender As Object, _
 
ByVal e As System.ComponentModel.PropertyChangedEventArgs)

  For index As Integer = 0 To Count - 1
    If ReferenceEquals(Me(index), sender) Then
     
Dim prop As PropertyDescriptor = TypeDescriptor.GetProperties(sender)(e.PropertyName)
     
OnListChanged(New System.ComponentModel.ListChangedEventArgs( _
             
ComponentModel.ListChangedType.ItemChanged, index, prop))
     
Exit For
   
End If
 
Next
End Sub

While I was looking at it, I believe some code in the SortedBindingList could be optomized to use the indexed .GetProperties.Index(string) overload rather than manually iterating over the collection. For example, couldn't ApplySort be shortened as follows:

Public Sub ApplySort( _
  
ByVal propertyName As String, _
  
ByVal direction As System.ComponentModel.ListSortDirection)

   mSortBy = Nothing
  
If Len(propertyName) > 0 Then
   'Recommended new code
   Dim prop As PropertyDescriptor = TypeDescriptor.GetProperties(GetType(T))(propertyName)
   End If
   ApplySort(prop,direction)
   'Replaces this:
   '  
Dim itemType As Type = GetType(T)
   '  
For Each prop As PropertyDescriptor In _
   '      TypeDescriptor.GetProperties(itemType)
   '     
If prop.Name = propertyName Then
   '        
mSortBy = prop
   '        
Exit For
   '     
End If
   '   Next
   '
End If
   '
ApplySort(mSortBy, direction)
End Sub

It appears that the first issue is a bug. The second code change example is just a recommendation.
Jim Wooley
http://devauthroity.com/blogs/jwooley

VB-MVP

RockfordLhotka replied on Thursday, February 15, 2007

I had made that first change at one point, then backed it out because the problem had seemed to resolve itself (without the change).

 

It has been a while since that occurred, and I don’t remember the specifics, but perhaps it needs to be revisited.

 

Rocky

 

jwooley replied on Friday, February 16, 2007

It appears to work without explicitly sending the property name in a non remoted environment, but something is not serializing properly across the dataportal in this instance. I’m using this in VB at the moment and suppose there could be a difference in the CSC which would show different behavior. It would be good if someone could test this in C# to confirm.

 

Jim

 

From: Rockford Lhotka [mailto:cslanet@lhotka.net]
Sent: Thursday, February 15, 2007 8:25 PM
To: jimwooley@hotmail.com
Subject: RE: [CSLA .NET] OnListChanged - PropertyDescriptor - Remoting vs. Non-Remoting

 

I had made that first change at one point, then backed it out because the problem had seemed to resolve itself (without the change).

 

It has been a while since that occurred, and I don’t remember the specifics, but perhaps it needs to be revisited.

 

Rocky

 



RockfordLhotka replied on Friday, February 16, 2007

It is important to realize that MY code is not used until after the object has been deserialized. In other words, Microsoft automatically hooks up the event and handles it in the normal case, but sadly they don’t re-hook the event on deserialization.

 

Rocky

 

From: Jim Wooley [mailto:cslanet@lhotka.net]
Sent: Friday, February 16, 2007 7:58 AM
To: rocky@lhotka.net
Subject: RE: [CSLA .NET] OnListChanged - PropertyDescriptor - Remoting vs. Non-Remoting

 

It appears to work without explicitly sending the property name in a non remoted environment, but something is not serializing properly across the dataportal in this instance. I’m using this in VB at the moment and suppose there could be a difference in the CSC which would show different behavior. It would be good if someone could test this in C# to confirm.

 

Jim

 

From: Rockford Lhotka [mailto:cslanet@lhotka.net]
Sent: Thursday, February 15, 2007 8:25 PM
To: jimwooley@hotmail.com
Subject: RE: [CSLA .NET] OnListChanged - PropertyDescriptor - Remoting vs. Non-Remoting

 

I had made that first change at one point, then backed it out because the problem had seemed to resolve itself (without the change).

 

It has been a while since that occurred, and I don’t remember the specifics, but perhaps it needs to be revisited.

 

Rocky

 

 



ZeroCoolJr replied on Tuesday, March 27, 2007

Hi,

Sorry to bump this up...was this fixed? I'm still using CSLA 2.0 in a non-remoting environment so I suspect lots of problems when we move to a remoting environment.

"...but sadly they don’t re-hook the event on deserialization.". Any workaround? Or did I missed something?

Thanks,
Win

RockfordLhotka replied on Tuesday, April 10, 2007

I've added the event handling code back into the current code, so the PropertyDescriptor should be included when the event is raised after deserialization. This only affects BLB and is current in svn.

skagen00 replied on Tuesday, April 10, 2007

Thanks for taking care of this Rocky. Nice to see you up and about (figuratively speaking).

 

Matt replied on Tuesday, October 30, 2007

Sorry to resurrect an old thread but I'm having the same problem described in this thread using CSLA 3.0.1.0 Did the above mentioned change make it into the 3.x builds?

RockfordLhotka replied on Wednesday, October 31, 2007

Matt:
Sorry to resurrect an old thread but I'm having the same problem described in this thread using CSLA 3.0.1.0 Did the above mentioned change make it into the 3.x builds?

The issue was discovered too late to make it into 3.0.2, which is the current build.

(having slipped "quick fixes" into previous releases at the 11th hour, I've learned my lesson - sometimes even bug fixes must wait for the next version...)

I may create a 3.0.3 maintenance release, though my focus now is on 3.5. Certainly this fix will be in 3.5, and if I do a 3.0.3 I'll probably put it there as well.

The whole thing occurs because I assumed that Microsoft would have done the flag check in their base OnListChanged() method. Logically you'd expect the check to occur at that level, because that's where the flag is maintained as well. Unfortunately their implementation apparently checks the flag each place where OnListChanged() is called rather than in OnListChanged() itself. This seems like poor design to me, but I'm sure there must be some good reason for it...

Or I could have seen an OnListChanged(honorFlag) overload, so a caller could choose to honor the raise events flag or not. But they didn't do that either. Ultimately this is probably what I will do though, because at least this technique centralizes the flag check into one location.

Matt replied on Wednesday, October 31, 2007

Rocky,

Thanks for the reply, I'm eagerly awaiting the 3.0.3 maintenence or the 3.5 ;)

In the mean time, I scanned the rest of the thread and looked through some old and new builds of csla to compare what's changed. Can anyone point me in the right direction to get this temporarily fixed? The current code in 3.0.2 looks like it should work according to previous posts that said passing the property descriptor into the ListChangedEventArgs would fix it. Were there problems with the original modification that will prevent it from running properly by using

PropertyDescriptor prop = TypeDescriptor.GetProperties(sender)(e.PropertyName);

to pass in the PropertyDescriptor instead of  the GetPropertyDescriptor method?

RockfordLhotka replied on Wednesday, October 31, 2007

“Fix it” meaning what?

 

The primary issue I’m aware of, and plan to address, is that the raise events flag isn’t honored so events are raised when they should be suppressed.

 

3.0 does include code to include the property descriptor when the event is raised. I believe someone had an issue there, but I haven’t had time to dig into that to find the nature of the issue.

 

Rocky

 

 

From: Matt [mailto:cslanet@lhotka.net]
Sent: Wednesday, October 31, 2007 4:07 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: OnListChanged - PropertyDescriptor - Remoting vs. Non-Remoting

 

Rocky,

Thanks for the reply, I'm eagerly awaiting the 3.0.3 maintenence or the 3.5 ;)

In the mean time, I scanned the rest of the thread and looked through some old and new builds of csla to compare what's changed. Can anyone point me in the right direction to get this temporarily fixed? The current code in 3.0.2 looks like it should work according to previous posts that said passing the property descriptor into the ListChangedEventArgs would fix it. Were there problems with the original modification that will prevent it from running properly by using

PropertyDescriptor prop = TypeDescriptor.GetProperties(sender)(e.PropertyName);

to pass in the PropertyDescriptor instead of  the GetPropertyDescriptor method?


Matt replied on Wednesday, October 31, 2007

I think my issue pertains to this part, in 3 tier mode the PropertyDescriptor comes back as null on a ListChanged event when I use PropertyHasChanged("Foo"); (works ok in 2 tier) Is this not happening a result of the raise event flag not being honored or are these two different problems?

RockfordLhotka:
3.0 does include code to include the property descriptor when the event is raised. I believe someone had an issue there, but I haven’t had time to dig into that to find the nature of the issue.

RockfordLhotka replied on Wednesday, October 31, 2007

These are two different problems.

 

Rocky

 

 

From: Matt [mailto:cslanet@lhotka.net]
Sent: Wednesday, October 31, 2007 4:26 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: OnListChanged - PropertyDescriptor - Remoting vs. Non-Remoting

 

I think my issue pertains to this part, in 3 tier mode the PropertyDescriptor comes back as null on a ListChanged event when I use PropertyHasChanged("Foo"); (works ok in 2 tier) Is this not happening a result of the raise event flag not being honored or are these two different problems?

RockfordLhotka:

3.0 does include code to include the property descriptor when the event is raised. I believe someone had an issue there, but I haven’t had time to dig into that to find the nature of the issue.



Matt replied on Wednesday, October 31, 2007

Ah, that makes more sense now.

I guess I'm interested in the reason the PropertyDescriptor isn't working properly in 3 tier mode. I know some of these posts are older, but is anyone else having this problem? And if you are, did you find a solution?

RockfordLhotka:

These are two different problems.


Rocky

RockfordLhotka replied on Wednesday, October 31, 2007

I took some time this evening to work on this, and I believe I have both issues fixed.

BusinessListBase no longer calls OnListChanged() if RaiseListChangedEvents is false. This should bring it into parity with normal BindingList(Of T) behaviors in this regard.

After deserialization, when BusinessListBase is explicitly raising ListChanged due to a child raising PropertyChanged, the correct property descriptor is now located. The result is that property descriptors should flow up after deserializiation like they do before serialization of the list in the first place.

These fixes are in 3.0.3 and 3.5. I put a test version of 3.0.3 on the download page (www.lhotka.net/cslanet/download.aspx). Since (thus far) this is the only meaningful change since 3.0.2, the "test" version should be pretty stable Smile [:)]

JoeFallon1 replied on Thursday, November 01, 2007

RockfordLhotka:

BusinessListBase no longer calls OnListChanged() if RaiseListChangedEvents is false. This should bring it into parity with normal BindingList(Of T) behaviors in this regard.

Rocky,

I had a similar problem with honoring the flag in this thread:

http://forums.lhotka.net/forums/thread/18781.aspx

Can you comment on the issue I faced? Do you think you fixed that too?

In other words if I change a value in a child collection is the method Child_PropertyChanged still called?

Joe

 

 

RockfordLhotka replied on Thursday, November 01, 2007

Yes, I believe my fix addresses that issue as well. The reason I say this, is that the child’s PropertyChanged event is handled in BLB, but if RaiseListChangedEvents is false, Child_PropertyChanged immediately exits and does nothing in response to the child’s event.

 

In my testing I observed parity between a non-deserialized list and a deserialized list when changing a child object’s property value – both with RaiseListChangedEvents set to true and false.

 

Rocky

 

 

From: JoeFallon1 [mailto:cslanet@lhotka.net]
Sent: Thursday, November 01, 2007 8:55 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: OnListChanged - PropertyDescriptor - Remoting vs. Non-Remoting

 

RockfordLhotka:

BusinessListBase no longer calls OnListChanged() if RaiseListChangedEvents is false. This should bring it into parity with normal BindingList(Of T) behaviors in this regard.

Rocky,

I had a similar problem with honoring the flag in this thread:

http://forums.lhotka.net/forums/thread/18781.aspx

Can you comment on the issue I faced? Do you think you fixed that too?

In other words if I change a value in a child collection is the method Child_PropertyChanged still called?

Joe

 

 



Matt replied on Thursday, November 01, 2007

Rocky,

Thank you very much for your quick responses and the new version!

Matt
RockfordLhotka:

I took some time this evening to work on this, and I believe I have both issues fixed.

BusinessListBase no longer calls OnListChanged() if RaiseListChangedEvents is false. This should bring it into parity with normal BindingList(Of T) behaviors in this regard.

After deserialization, when BusinessListBase is explicitly raising ListChanged due to a child raising PropertyChanged, the correct property descriptor is now located. The result is that property descriptors should flow up after deserializiation like they do before serialization of the list in the first place.

These fixes are in 3.0.3 and 3.5. I put a test version of 3.0.3 on the download page (www.lhotka.net/cslanet/download.aspx). Since (thus far) this is the only meaningful change since 3.0.2, the "test" version should be pretty stable Smile [:)]

ben replied on Wednesday, September 19, 2007

Hi all,

 

Here you have a small application for testing this kind of problems. 

 

http://forums.lhotka.net/forums/thread/17690.aspx

 

I hope you find it useful.

 

Benjamin Moles

Copyright (c) Marimer LLC