INotifyPropertyChanged: automatic dependent property and nested object support

Summary

The hardest problem with INotifyPropertyChanged is often overlooked: dependency management. When one property is changed, all of its dependent properties need to fire as well.

This blog presents another solution based on dynamic dependency determination.

Table of contents

Introduction

As stated by Michael L Perry in a comment on my blog INotifyPropertyChanged implementations: An Overview:


The hardest problem with INotifyPropertyChanged is often overlooked: dependency management. When one property is changed, all of its dependent properties need to fire as well.

Here are some solutions other people have come up with:

This blog presents another solution based on dynamic dependency determination.

The problem

Introduction

There are two problems to solve:

  • How to generate INotifyPropertyChanged notifications for dependent properties
  • How to generate INotifyPropertyChanged notifications for nested object hierarchies

The following paragraphs give examples of both kind of problems.

Dependent properties

Suppose there is a Person class that contains the properties FirstName, LastName and the calculated property FullName. When for instance the first name of a person changes, obviously the full name also changes. This class could be implemented like this using the BindableObjectBase from this post:

public class Person : BindableObjectBase
{
    private string firstName;
    private string lastName;
 
    public Person(string firstName, string lastName)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
    }
 
    public string FirstName
    {
        get
        {
            return this.firstName;
        }
        set
        {
            if (this.SetValue(ref this.firstName, value, "FirstName"))
            {
                this.OnPropertyChanged("FullName");
            }
        }
    }
 
    public string LastName
    {
        get
        {
            return this.lastName;
        }
        set
        {
            if (this.SetValue(ref this.lastName, value, "LastName"))
            {
                this.OnPropertyChanged("FullName");
            }
        }
    }
 
    public string FullName
    {
        get
        {
            return this.FirstName + " " + this.LastName;
        }
    }
}

The problem lies in the manual addition of the OnPropertyChanged calls. When classes get complex, it can be a nightmare to maintain.

Nested objects

Now suppose there is also a HelloMessage that is defined as follows:

public class HelloMessage : BindableObjectBase
{
    private Person person;
 
    public HelloMessage(Person person)
    {
        this.Person = person;
    }
 
    public Person Person
    {
        get
        {
            return this.person;
        }
        set
        {
            if (this.SetValue(ref this.person, value, "Person"))
            {
                this.OnPropertyChanged("Message");
            }
        }
    }
 
    public string Message
    {
        get
        {
            return "Hello " + this.Person.FullName;
        }
    }
}

Now this code

Person person = new Person("E", "Jongerius");
HelloMessage message = new HelloMessage(person);
message.PropertyChanged += (s, e) =>
{
    Console.WriteLine("* Changed: " + e.PropertyName);
};
 
// Display the message
Console.WriteLine(message.Message);
 
// Change first name
person.FirstName = "Emiel";
 
// Display the message again
Console.WriteLine(message.Message);

results in

Hello E Jongerius
Hello Emiel Jongerius

The message.PropertyChanged event is not raised, although the value of message.Message has been changed.

The usual way to approach this problem is to replace the definition of the Person property with the following:

public Person Person
{
    get
    {
        return this.person;
    }
    set
    {
        INotifyPropertyChanged oldValue = this.person as INotifyPropertyChanged;
        INotifyPropertyChanged newValue = value as INotifyPropertyChanged;
 
        if (this.SetValue(ref this.person, value, "Person"))
        {
            this.OnPropertyChanged("Message");
 
            if (oldValue != null)
            {
                oldValue.PropertyChanged -= this.OnPersonChanged;
            }
            if (newValue != null)
            {
                newValue.PropertyChanged += this.OnPersonChanged;
            }
        }
    }
}
 
private void OnPersonChanged(object s, PropertyChangedEventArgs e)
{
    this.OnPropertyChanged("Person");
    this.OnPropertyChanged("Message");
}

Again, this can result in a maintenance nightmare when object hierarchies become complex.

Solution

The basic idea of the solution described in this blog is that the base class keeps track of the dependencies between the properties. This information is determined dynamically at runtime, during the calls to the property getters. Whenever the value of a property is changed, the previously determined information is used to raise change notifications for dependent properties.

Also the property values are checked whether they implement the INotifyPropertyChanged interface. If so, the appropriate event handlers are attached and detached during the setting of the property. A similar strategy is used by Caliburn.

There are two important things to notice about this approach:

  1. Dependencies are determined dynamically at runtime, so a performance hit is likely to be expected
  2. Dependencies are updated only when property getters are called. This implies that when the value of a property has not been retrieved, the dependency information regarding this property has not been determined. Normally this would be no problem, because if the value of a property has not been used, the application is probably also not interested in the fact that the value of this property has been changed

Implementation

Base class

The solution uses the BindableObjectBase class from this post as the base class with the following modifications:

  • The function OnPropertyChanged(string propertyName) has been made virtual
  • The static function GetPropertyNameFromStackTrace() has been made protected
  • The static function GetPropertyNameFromExpression<T>(Expression<Func<T>> property) has been made protected
  • Refactored the functions SetValue<T>(ref T local, T newValue, Expression<Func<T>> property) and SetValue<T>(ref T local, T newValue) to make use of the function SetValue<T>(ref T local, T newValue, string propertyName)
  • The function SetValue<T>(ref T local, T newValue, string propertyName) has been made virtual

Storage of dependency information

The dependency information is stored as follows:

private static IDictionary<string, IDictionary<string, ISet<string>>> globalPropertyDependencyCollection = new Dictionary<string, IDictionary<string, ISet<string>>>();
 
private IDictionary<string, ISet<string>> propertyDependencyCollection;
private IDictionary<string, ISet<string>> PropertyDependencyCollection
{
    get
    {
        if (this.propertyDependencyCollection == null)
        {
            if (!BindableObjectBase2.globalPropertyDependencyCollection.TryGetValue(this.GetType().FullName, out this.propertyDependencyCollection))
            {
                this.propertyDependencyCollection = new Dictionary<string, ISet<string>>();
                BindableObjectBase2.globalPropertyDependencyCollection[this.GetType().FullName] = this.propertyDependencyCollection;
            }
        }
        return this.propertyDependencyCollection;
    }
}

The private static variable globalPropertyDependencyCollection is used to reused dependency information between instances of the same type. Access to the global dependency information is cached using the propertyDependencyCollection instance variable.

Adding dependency information is relatively straight forward:

private void AddPropertyDependency(string property, string dependsOn)
{
    ISet<string> propertyDependencies = null;
    if (!this.PropertyDependencyCollection.TryGetValue(dependsOn, out propertyDependencies))
    {
        propertyDependencies = new HashSet<string>();
    }
    else
    {
        if (propertyDependencies.Contains(property))
        {
            return;
        }
    }
 
    propertyDependencies.Add(property);
 
    this.PropertyDependencyCollection[dependsOn] = propertyDependencies;
}

Notify changes

Based on the stored dependency information, change notifications regarding dependent properties can be automatically sent using this override to OnPropertyChanged:

protected override void OnPropertyChanged(string propertyName)
{
    base.OnPropertyChanged(propertyName);
 
    ISet<string> dependsOn = null;
    if (!this.PropertyDependencyCollection.TryGetValue(propertyName, out dependsOn))
    {
        return;
    }
 
    foreach (string item in dependsOn)
    {
        base.OnPropertyChanged(item);
    }
}

Property Get call stack

Before the actually dependencies can be determined, an additional piece of infrastructure has to be put in place. Whenever a property getter is called from within another getter, a dependency between these two properties must be added. To keep track of property getter calls, a call stack is implemented:

private Stack<string> getCallStack = new Stack<string>();

Based on the contents of this call stack, the dependencies can be added using the following function:

private void PropertyGetCalled(string propertyName)
{
    foreach (string p in this.getCallStack)
    {
        this.AddPropertyDependency(p, propertyName);
    }
}

Determining dependencies

In order to determine the dependencies the following private class is used:

private class PropertyTracker : IDisposable
{
    private bool isDisposed = false;
    private BindableObjectBase2 containingObject;
 
    public PropertyTracker(string propertyName, BindableObjectBase2 objectBase)
    {
        this.containingObject = objectBase;
        this.containingObject.PropertyGetCalled(propertyName);
        this.containingObject.getCallStack.Push(propertyName);
    }
 
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
 
    private void Dispose(bool disposing)
    {
        if (this.isDisposed)
        {
            return;
        }
 
        this.containingObject.getCallStack.Pop();
        this.containingObject = null;
        this.isDisposed = true;
    }
 
    ~PropertyTracker()
    {
        Dispose(false);
    }
}

This class serves two functions:

  1. It calls the function PropertyGetCalled on the containing object, which adds the dependencies to dependent properties
  2. It pushes the property name on the call stack and makes sure that the name is removed whenever the class gets disposed.

As long as for each property an instance of this class is created at the start of the getter and this instance is disposed at the end of the getter, the getCallStack will be filled correct and the correct references will be added using the call to PropertyGetCalled.

To facilitate the use of the class the following helper functions have been defined:

protected IDisposable GetPropertyTracker()
{
    return this.GetPropertyTracker(GetPropertyNameFromStackTrace());
}
 
protected IDisposable GetPropertyTracker<T>(Expression<Func<T>> property)
{
    return this.GetPropertyTracker(GetPropertyNameFromExpression(property));
}
 
protected IDisposable GetPropertyTracker(string propertyName)
{
    return new PropertyTracker(propertyName, this);
}

Nested object support

With the following additions it is possible to extend this solution to support nested objects:

private Dictionary<string, PropertyChangedEventHandler> propertyChangedEventHandlers = new Dictionary<string, PropertyChangedEventHandler>();
 
protected override bool SetValue<T>(ref T local, T newValue, string propertyName)
{
    INotifyPropertyChanged localAsBindableObject = local as INotifyPropertyChanged;
    INotifyPropertyChanged newValueAsBindableObject = newValue as INotifyPropertyChanged;
 
    if (!base.SetValue<T>(ref local, newValue, propertyName))
    {
        return false;
    }
 
    if (localAsBindableObject != null)
    {
        PropertyChangedEventHandler handler = null;
        if (propertyChangedEventHandlers.TryGetValue(propertyName, out handler))
        {
            this.propertyChangedEventHandlers.Remove(propertyName);
            localAsBindableObject.PropertyChanged -= handler;
        }
    }
 
    if (newValueAsBindableObject != null)
    {
        PropertyChangedEventHandler handler = (s, e) => { OnPropertyChanged(propertyName); };
 
        this.propertyChangedEventHandlers[propertyName] = handler;
        newValueAsBindableObject.PropertyChanged += handler;
    }
 
    return true;
}

The basic idea behind this code is that whenever a value of a property raises the PropertyChanged event, we can assume that that property has changed, so we can raise the PropertyChanged event for the object containing the property too. This is done by attaching a PropertyChanged handler to all objects that support the INotifyPropertyChanged interface whenever they are used as a value of a property and to remove the handler whenever the property gets another value.

Preventing a PopertyChanged event storm

Executing this example with the base objects that implement the techniques described above, gives the following results:

* Changed: Person
* Changed: Message
* Changed: Person
* Changed: Message

The PropertyChanged event gets fired twice for each property. This is caused by the following chaing of events:

The statement peson.FirstName = "Emiel"; causes person.PropertyChanged("FirstName") to be raised, which causes

  1. message.PropertyChanged("Person") to be raised, which causes message.PropertyChanged("Message") to be raised.
  2. person.PropertyChanged("FullName") to be raised, which causes message.PropertyChanged("Person") to be raised, which causes message.PropertyChanged("Message") to be raised.

The solution to this problem is to reorder the generated PropertyChanged events and remove the duplicates during the reordering. This strategy is implemented in the following class:

public class BindableObjectBase3 : BindableObjectBase2
{
    private static IDictionary<BindableObjectBase3, ISet<string>> delayedNotifications = null;
    private static BindableObjectBase3 currentObject = null;
 
    protected override void OnPropertyChanged(string propertyName)
    {
        bool isFirst = (delayedNotifications == null);
 
        try
        {
            if (isFirst)
            {
                delayedNotifications = new Dictionary<BindableObjectBase3, ISet<string>>();
                currentObject = this;
 
                base.OnPropertyChanged(propertyName);
            }
            else
            {
                if (this == currentObject)
                {
                    base.OnPropertyChanged(propertyName);
                }
                else
                {
                    this.OnPropertyChangedDelayed(propertyName);
                }
            }
        }
        finally
        {
            if (isFirst)
            {
                try
                {
                    ProcessDelayedNotifications();
                }
                finally
                {
                    delayedNotifications = null;
                    currentObject = null;
                }
            }
        }
    }
 
    private void OnPropertyChangedDelayed(string propertyName)
    {
        ISet<string> delayedNotificationsForThis = null;
 
        if (!delayedNotifications.TryGetValue(this, out delayedNotificationsForThis))
        {
            delayedNotificationsForThis = new HashSet<string>();
            delayedNotifications.Add(this, delayedNotificationsForThis);
        }
 
        delayedNotificationsForThis.Add(propertyName);
    }
 
    private void ProcessDelayedNotifications()
    {
        if (delayedNotifications == null)
        {
            return;
        }
 
        while (delayedNotifications.Any())
        {
            KeyValuePair<BindableObjectBase3, ISet<string>> firstItem = delayedNotifications.ElementAt(0);
            delayedNotifications.Remove(firstItem.Key);
 
            currentObject = firstItem.Key;
            foreach (string propertyName in firstItem.Value)
            {
                currentObject.OnPropertyChanged(propertyName);
            }
        }
    }
}

Usage

The solution presented in this post is easy to use:

  1. Derive all business objects from BindableObjectBase3
  2. Wrap all property getters with a using statement which contains a new instance of the PropertyTracker class created by one of the overloads of GetPropertyTracker
  3. Use one of the overloads of SetValue in the property setters

That's it!

Example

This sample in this paragraph can also be found in the code download.

Note: in this example the strategy using lambda expressions and expression trees to determine the names of properties is used. All other strategies described here could be used too.

Class Person

public class Person : BindableObjectBase3
{
    private string firstName;
    private string lastName;
 
    public Person(string firstName, string lastName)
    {
        this.FirstName = firstName;
        this.LastName = lastName;
    }
 
    public string FirstName
    {
        get
        {
            using (this.GetPropertyTracker(() => this.FirstName))
            {
                return this.firstName;
            }
        }
            set
            {
                this.SetValue(ref this.firstName, value, () => this.FirstName);
            }
        }
 
        public string LastName
        {
            get
            {
                using (this.GetPropertyTracker(() => this.LastName))
                {
                    return this.lastName;
                }
            }
            set
            {
                this.SetValue(ref this.lastName, value, () => this.LastName);
            }
        }
 
    public string FullName
    {
        get
        {
            using (this.GetPropertyTracker(() => this.FullName))
            {
                return this.FirstName + " " + this.LastName;
            }
        }
    }
}

Class HelloMessage

public class HelloMessage : BindableObjectBase3
{
    private Person person;
 
    public HelloMessage(Person person)
    {
        this.Person = person;
    }
 
    public Person Person
    {
        get
        {
            using (this.GetPropertyTracker(() => this.Person))
            {
                return this.person;
            }
        }
        set
        {
            this.SetValue(ref this.person, value, () => this.Person);
        }
    }
 
    public string Message
    {
        get
        {
            using (this.GetPropertyTracker(() => this.Message))
            {
                return "Hello " + this.Person.FullName;
            }
        }
    }
}

Test

Person person1 = new Person("E.", "Jongerius");
 
person1.PropertyChanged += (s, e) =>
{
    Console.WriteLine(String.Format("   // Raised: person1.PropertyChanged(\"{0}\");", e.PropertyName));
};
 
HelloMessage message = new HelloMessage(person1);
 
message.PropertyChanged += (s, e) =>
{
    Console.WriteLine(String.Format("   // Raised: message.PropertyChanged(\"{0}\");", e.PropertyName));
};
 
person1.FirstName = "Emiel";
   // Raised: person1.PropertyChanged("FirstName");
   // Raised: message.PropertyChanged("Person");
 
// The following line causes the dependency information
// to be updated
string msg = message.Message;
 
person1.FirstName = "E.";
   // Raised: person1.PropertyChanged("FirstName");
   // Raised: person1.PropertyChanged("FullName");
   // Raised: message.PropertyChanged("Person");
   // Raised: message.PropertyChanged("Message");
 
Person person2 = new Person("Someone", "Else");
 
person2.PropertyChanged += (s, e) =>
{
    Console.WriteLine(String.Format("   // Raised: person2.PropertyChanged(\"{0}\");", e.PropertyName));
};
 
message.Person = person2;
   // Raised: message.PropertyChanged("Person");
   // Raised: message.PropertyChanged("Message");
 
person1.FirstName = "Emiel";
   // Raised: person1.PropertyChanged("FirstName");
   // Raised: person1.PropertyChanged("FullName");
 
person2.FirstName = "S.";
   // Raised: person2.PropertyChanged("FirstName");
   // Raised: person2.PropertyChanged("FullName");
   // Raised: message.PropertyChanged("Person");
   // Raised: message.PropertyChanged("Message");

Download

All sources can be downloaded here

Final words

The implementation given above has some limitations:

  • The implementation is not theadsafe
  • It is possible to create object hierarchies that cause endless loops during PropertyChanged events.

18 Responses to “INotifyPropertyChanged: automatic dependent property and nested object support”

  1. Rob Eisenberg said:

    Jul 03, 10 at 1:22 am

    Interestingly, this is very similar to what Caliburn’s INPC aspect does. We use the same sort of strategy to “record” dependent properties.

  2. Michael L Perry said:

    Jul 03, 10 at 2:43 am

    This is very similar to the way that Update Controls dynamically discovers dependencies. The biggest difference is that BindableObjectBase3 does its record keeping in the base class, whereas Dependent (the Update Controls workhorse) favors composition.

    I solved the event storm problem by doing two things. First, when a dependent property goes out of date, it unwinds all dependencies. This prevents a secondary change from triggering it again. Second, all PropertyChanged events are invoked asynchronously. This gives everything a chance to settle down before the UI comes asking for up-to-date values.

    Interestingly enough, invoking PropertyChanged asynchronously is also a requirement for thread safety. The event has to be raised on the UI thread. But that’s not quite enough. You have to handle the case where the background thread is making something out-of-date while the foreground thread is bringing it up-to-date. I had to do quite a bit of math to get that algorithm right.

    Finally, the endless loops are easy to spot. You already have a stack of properties being accessed. Just check that stack on entry to see if the property is already there. I solved it by setting the state of a Dependent to UPDATING while it’s in the stack.

    Cool implementation of automatic dependency management. Thanks for letting me compare notes.

  3. Michael L Perry said:

    Jul 06, 10 at 8:26 pm

    I’ve reviewed the Caliburn implementation and found that there is one striking commonality between your implementation and Rob’s. Both systems use property names to record dependencies.

    In Caliburn, the NotificationProfile class records (among other things) property dependencies by type. It keeps the information in a Dictionary<string, IList>. This is very similar to your IDictionary<string, ISet> in BindableObjectBase.

    What concerns me about this is that the ViewModel might depend upon properties of several different objects. A string alone cannot tell you which instance that dependency comes from. If any of the precedent objects fires, the ViewModel will respond.

    Does this make sense? Do you share my concern?

  4. Emiel said:

    Jul 07, 10 at 9:40 am

    @Rob: Thanks, I didn’t know that. I have changed my blog to include a reference to Caliburn.

  5. Emiel said:

    Jul 07, 10 at 10:19 am

    @Michael: thanks for your insight in the Update Controls implementation.

    Regarding your concern: I don’t know the details of the Caliburn implementation, so I cannot say something about that. But for the implementation I have presented above I do not share it.

    The variable globalPropertyDependencyCollection is used to record dependencies within the object itself. For example: FullName depends on LastName in type Person. This holds for all instances of the Person.

    The variable delayedNotifications is a dictionary of instances which contains sets of event notifications that have to be raised on the instances themselves (see ProcessDelayedNotifications).
    This ensures that the correct sender is used in the events.

    Or am I missing your point here?

  6. Michael L Perry said:

    Jul 09, 10 at 2:52 am

    I missed the globalPropertyDependencyCollection variable. Combined with delayedNotifications, it covers my concern in most cases. It would be rare that a property depends on different subsets of different instances of the same type. I could synthesize an example, but it would be contrived.

    So essentially its a memory trade off. Whereas Update Controls tracks dependencies to the field instance, your solution and Rob’s track them to the object instance and use metadata to fill in the rest.

  7. Emiel said:

    Jul 09, 10 at 10:31 am

    Also, I think it is a matter of taste. I think the solution presented here is simpler to use: all properties are treated equally and there is no need for constructs like

    DataContext = ForView.Wrap(new PersonViewModel(new Person()));

    But then again: De gustibus non est disputandum

  8. DotNetShoutout said:

    Jul 10, 10 at 7:49 am

    On developing Pochet.NET» Blog Archive » INotifyPropertyChanged: automatic dependent property and nested object support…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  9. Nathan Risto said:

    Aug 19, 10 at 8:38 pm

    This has been really helpful in resolving a lot of my issues.

    Just one question. What is the best way to call your methods when I have a method that updates a field directly (by not using the setter/getter of the property encapsulating the field)? I have plenty of situations where for one reason or another (driving object behavior, etc…) the field has to be used internally in the object rather than the property. Do I call one of your overloads to accomplish this scenario?

    #region Fields
     
    private string m_strName;
     
    #endregion
     
    #region Properties
     
    public virtual string Name
    {
        get
        {
            using (GetPropertyTracker(() =&gt; this.Name))
            {
                return m_strName;
            }
        }
        set
        {
            this.SetValue(ref this.m_strName, value, () =&gt; this.Name);
        }
    }
     
    #endregion
     
    #region Methods
     
    public void ChangeName(string name)
    {
        m_strName = name;
     
        //what do I need to do here to indicate that the Name property has changed?
    }
     
    #endregion
  10. Nathan Risto said:

    Aug 19, 10 at 9:16 pm

    I’ve tried both of these situations, but neither seemed to cause the Name property to update what was bound to it.

    public void ChangeName(string name)
    {
    	using (GetPropertyTracker("Name"))
    	{
    		this.SetValue(ref m_strName, name, "Name");
    	}
    }
     
    public void ChangeName(string name)
    {
    	using (GetPropertyTracker(() =&gt; this.Name))
    	{
    		this.SetValue(ref m_strName, name, () =&gt; this.Name);
    	}
    }
  11. Nathan Risto said:

    Aug 19, 10 at 9:33 pm

    Nevermind. I had a mistake else where. The code I posted above works fine.

  12. Emiel said:

    Aug 19, 10 at 10:31 pm

    I am glad I could be of help :)

  13. Nathan Risto said:

    Sep 07, 10 at 8:18 pm

    Just out of curiosity. Have you tested this in multi-threaded use? I’ve built a library of objects inheriting from your base objects. When I run a Parrellel.ForEach() on them, I run into errors within your code. Currently I don’t have time to investigate specifically what’s happening, but I plan on revisiting the issue when my work load permits. So I was just wondering if you had anything to add on the subject. Thanks again.

  14. Emiel said:

    Sep 07, 10 at 8:57 pm

    No, I have not tested this in multi-threaded use. What kind of errors do you run into?

    Please be aware that the INotifyPropertyChanged notifications are most often used in databindings in the UI and that UI updates from a non-UI thread leads to errors like Invalid cross-thread access. If this is the source of your errors, you can resolve them by wrapping the UI updates in Dispatcher.Invoke calls like this:

    Dispatcher.BeginInvoke( () => 
        {
            // Stetements to update the UI
        });
  15. Nathan Risto said:

    Feb 08, 11 at 10:22 pm

    Yeah. We tried that. Unfortunately, our knowledge and experience in dealing with multi-threaded logic is quite limited. So we aren’t sure if we are using that code correctly. But we tried various implementations and we unsuccessful.

    The objects we are working with inherit from your classes. We have a collection of them. And we are using Parrallel.Foreach() to try and run a set of checks on each item. However, we get errors in your code when doing so.

    For instance, in the PropertyGetCalled method, we see an InvalidOperationException – Collection was modified after the enumerator was instantiated.

  16. Nathan Risto said:

    Feb 08, 11 at 10:53 pm

    Other errors we see when trying to run in Parallel are in the OnPropertyChangedDelayed where it complains that BindableObject.delayedNotifications is a null reference. So we can’t call the TryGetValue() method from it.

  17. Pasi said:

    May 12, 11 at 3:04 pm

    Any news on how to make it work in a multi-threaded scenario?

  18. On developing Pochet.NET» Blog Archive » INotifyPropertyChanged implementations: An Overview said:

    Jul 09, 12 at 10:56 am

    [...] INotifyPropertyChanged: automatic dependent property and nested object support [...]