| Severity | Warning |
| Reported by | Metalama.Patterns.Observability |
| Message | The '<Property>' property cannot be observed because '<DeclaringType>' does not implement INotifyPropertyChanged. Consider implementing the INotifyPropertyChanged interface in '<DeclaringType>', marking '<DeclaringType>' with [Constant], or using ConfigureObservability via a fabric. |
Cause
The [Observable] aspect updates a computed property when the properties it depends on change. For a property of a base type, the aspect relies on the base type raising the PropertyChanged event.
Metalama reports LAMA5166 when a computed property of an observable type reads a property of a base class that doesn't implement INotifyPropertyChanged. In the message, <Property> is the property of the base type and <DeclaringType> is the base type. The aspect can't detect changes to this property, so the computed property isn't notified.
If a computed property reads a child of another computed property of the same type, Metalama reports LAMA5163 instead.
In the following example, Greeting reads FirstName, a property of the Person base class that isn't observable.
1using Metalama.Patterns.Observability;
2
3namespace Doc.LAMA5166.Error;
4
5// This base class does not implement INotifyPropertyChanged.
6public class Person
7{
8 public string FirstName { get; set; } = "";
9}
10
11[Observable]
12public partial class Employee : Person
13{
14 // Warning: changes to FirstName cannot be observed.
Warning LAMA5166: The 'Person.FirstName' property cannot be observed because 'Person' does not implement INotifyPropertyChanged. Consider implementing the INotifyPropertyChanged interface in 'Person', marking 'Person' with [Constant], or using ConfigureObservability via a fabric.
15 public string Greeting => "Hello, " + this.FirstName;
16}
17
1using System.ComponentModel;
2using Metalama.Patterns.Observability;
3
4namespace Doc.LAMA5166.Error;
5
6// This base class does not implement INotifyPropertyChanged.
7public class Person
8{
9 public string FirstName { get; set; } = "";
10}
11
12[Observable]
13public partial class Employee : Person, INotifyPropertyChanged
14{
15 // Warning: changes to FirstName cannot be observed.
16 public string Greeting => "Hello, " + this.FirstName;
17
18 protected virtual void OnPropertyChanged(string propertyName)
19 {
20 switch (propertyName)
21 {
22 case "FirstName":
23 OnPropertyChanged("Greeting");
24 break;
25 }
26
27 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
28 }
29
30 public event PropertyChangedEventHandler? PropertyChanged;
31}
32
Resolution
Do one of the following:
- Apply the [Observable] aspect to the base type, as in the following example, or implement INotifyPropertyChanged in it manually.
- If you cannot change the base type, follow the other suggestions of the message: mark the property as constant, or configure the observability of the property with ConfigureObservability in a fabric.
1using Metalama.Patterns.Observability;
2
3namespace Doc.LAMA5166.Fixed;
4
5// Fixed: the base class is observable.
6[Observable]
7public partial class Person
8{
9 public string FirstName { get; set; } = "";
10}
11
12[Observable]
13public partial class Employee : Person
14{
15 public string Greeting => "Hello, " + this.FirstName;
16}
17
1using System.ComponentModel;
2using Metalama.Patterns.Observability;
3
4namespace Doc.LAMA5166.Fixed;
5
6// Fixed: the base class is observable.
7[Observable]
8public partial class Person : INotifyPropertyChanged
9{
10 private string _firstName = "";
11
12 public string FirstName
13 {
14 get
15 {
16 return _firstName;
17 }
18
19 set
20 {
21 if (!object.ReferenceEquals(value, _firstName))
22 {
23 _firstName = value;
24 OnPropertyChanged("FirstName");
25 }
26 }
27 }
28
29 protected virtual void OnPropertyChanged(string propertyName)
30 {
31 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
32 }
33
34 public event PropertyChangedEventHandler? PropertyChanged;
35}
36
37[Observable]
38public partial class Employee : Person
39{
40 public string Greeting => "Hello, " + this.FirstName;
41
42 protected override void OnPropertyChanged(string propertyName)
43 {
44 switch (propertyName)
45 {
46 case "FirstName":
47 OnPropertyChanged("Greeting");
48 break;
49 }
50
51 base.OnPropertyChanged(propertyName);
52 }
53}
54