3

I've run into a really strange problem I can't seem to reproduce with a small example. Sorry if this question is a little vague.

I have a Person which contains an Address. Both inherit from BaseEntity which implements INotifyPropertyChanged. I want the Person class to NotifyPropertyChanged("Address") not only when an Address is set, but also when that Address itself changes, so my get/set in Person looks like this:

class Person : BaseEntity
{
    private Address address;
    public Address Address
    {
        get { return address; }
        set
        {
            address = value;
            NotifyPropertyChanged("Address");

            // propagate changes in Address to changes in Person
            address.PropertyChanged += (s, e) => { NotifyPropertyChanged("Address"); };
        }
    }

    ...
}

This has worked nicely for months.

I've added [Serializable] to Person, Address, and BaseEntity (and [field: NonSerialized] to BaseEntity's PropertyChanged), and now when I make a change to Address (somePerson.Address.Street = "something new") that address's PropertyChanged's invocationCount is 0 where it used to be 1, so Person doesn't get notified, and doesn't itself fire NotifyPropertyChanged("Address");

Again, if I remove [Serializable] from Person, it works, and if I add it back, it doesn't work. I'm not actually serializing anything yet, I've just added the [Serializable] attribute.

Any ideas?

epalm
  • 4,283
  • 4
  • 43
  • 65

4 Answers4

2

Are your Person/Address/BaseEntity being serialized/deserialized and then exhibiting this behavior, or is it simply the additional of the [Serializable] attribute that is causing this behavior?

I ask if the objects are being deserialized and then exhibiting the behavior becaseu in most of my implementations of INotifyPropertyChanged I explicitly mark the PropertyChanged event as being non-serialized and then manually re-hook the event upon deserialization as appropriate. (Having events serialized expands the object-graph and can cause serialization of unexpected objects.)

If your object's are not serializing the event, then upon deserialization, it would make sense that they're not appearing to fire. They're probably being raised, but nobody is listening any more.

Yoopergeek
  • 5,592
  • 3
  • 24
  • 29
  • Also worth noting is that BinaryFormatter serializes fields, not properties...so upon deserialziation, the property setter won't be called and won't rehook the PropertyChanged event. – Jeff Jul 14 '11 at 18:15
  • Good point, I should have clarified that my comments are from my experiences with the BinaryFormatter. – Yoopergeek Jul 14 '11 at 18:16
  • Simply the addition of the [Serializable] attribute that is causing this behavior. – epalm Jul 14 '11 at 18:31
  • @epalm unless there is some serialization involved, I find that hard to credit... Would really need to see a reproducible example. My advice - take a copy of your broken version and make it simpler one piece at a time until it works - hey presto, solved (I.e. you'll have found the key difference) – Marc Gravell Jul 14 '11 at 18:47
  • @epalm note that adding the object to a cache or similar may involve serialization – Marc Gravell Jul 14 '11 at 18:50
  • @Marc, I started the other way, building a simple example up to "pretty much" what I've got, which is what I meant by a "problem I can't seem to reproduce with a smaller example". I know "pretty much" isn't "identical". Why would it work without [Serializable] though? Could something be getting serialized without my knowledge? Could that even happen without [Serializable]? Again, I understand why you find it hard to credit, but I'm not serializing anything myself, and by simply adding [Serializable] things are breaking. I find it hard to credit too, which is why I'm asking here! – epalm Jul 14 '11 at 19:21
  • 2
    @epalm - easy way to find out - implement ISerializable (and add the appropriate constructor), and put a break-point in there. That'll tell you if it got serialized. Or maybe even just add a serialization callback. – Marc Gravell Jul 14 '11 at 19:30
  • @Marc, added a serialization callback. Simply adding [Serializable] to Person IS causing it to be serialized somewhere by something. I don't even know what. That's annoying. I guess I'll re-wire the events in [OnSerialized]. – epalm Jul 14 '11 at 20:08
  • @epalm - in your break-point, open the call-stack window. Then walk backwards until you find the offending code. – Marc Gravell Jul 14 '11 at 20:11
  • Are you sending this instance across AppDomains? – Jeff Jul 14 '11 at 20:34
2

My guess (slightly spurred on from a discussion in the comments) is that some code somewhere in your application is detecting [Serializable] and deciding to serialize the object. For example, a cache would be a likely candidate - as might any "deep clone" code.

Try implementing ISerializable (or just add a serialization callback), and add a break-point. If your break-point hits, load the call-stack window and navigate back up the stack to see what is serializing your object, and why.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I added an [OnSerialized] callback with a breakpoint, here's the stacktrace: http://dl.dropbox.com/u/4220513/stacktrace.txt. Looks like WCF maybe, who says "oh hey, it's Serializable so I guess I'll serialize it now" and because I had [field: NonSerialized] on BaseEntity's PropertyChanged event, the event handlers were being destroyed (upon deserialization, I guess). – epalm Jul 15 '11 at 14:09
1

See if adding:

[field:NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;

does the trick.

Originally from: How to exclude nonserializable observers from a [Serializable] INotifyPropertyChanged implementor?

Community
  • 1
  • 1
cfeduke
  • 23,100
  • 10
  • 61
  • 65
1

I did a little test and there was no difference between having the classes serializable and not. Here is some working sample code.

    [Serializable]
    public class BaseEntity : INotifyPropertyChanged
    {
        [NonSerialized]
        private PropertyChangedEventHandler _propertyChanged;

        public event PropertyChangedEventHandler PropertyChanged
        {
            add { _propertyChanged += value; }
            remove { _propertyChanged -= value; }
        }

        protected void NotifyPropertyChanged(string propertyName)
        {
            _propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    [Serializable]
    public class Person : BaseEntity
    {
        private Address _address;

        public Address Address
        {
            get { return _address; }
            set
            {
                _address = value;
                NotifyPropertyChanged("Address");
                Address.PropertyChanged += (s, e) =>
                                               {
                                                   Console.WriteLine("Address Property Changed {0}", e.PropertyName);
                                                   NotifyPropertyChanged("Address");
                                               };
            }
        }

    }
    [Serializable]
    public class Address : BaseEntity
    {
        private string _city;
        public string City
        {
            get { return _city; }
            set
            {
                _city = value;
                NotifyPropertyChanged("City");
            }
        }
    }
    static void Main(string[] args)
    {



        var person = new Person();
        person.PropertyChanged += (s, e) => Console.WriteLine("Property Changed {0}", e.PropertyName);
        person.Address = new Address();
        person.Address.City = "TestCity";

    }

Program output is

Property Changed Address

Address Property Changed City

Property Changed Address

Darryl Braaten
  • 5,229
  • 4
  • 36
  • 50
  • simply the addition of the [Serializable] attribute that is causing this behavior. I'm not (actively) serializing anything. – epalm Jul 14 '11 at 18:57
  • @epalm I wrote a little test application and could not reproduce the error you are seeing. You may want to compare what I did to your actual code to see if there are any material differences – Darryl Braaten Jul 14 '11 at 19:28