Entity Framework Doesn't Support INotifyPropertyChanged on Some Properties

If you are using the ADO.NET Entity Framework, you may have noticed that it doesn't always raise a "PropertyChanged" event.  More specifically, the "OnPropertyChanged" event is only called for scalar properties, not Navigation properties.

Lets take a closer look and I'll show you exactly what I mean.  Consider this very simple EF model:



Consider a scalar property first, such as the "firstName" property of a "User" entity.  If I set this in my code, the PropertyChanged event will fire.  It fires because the code-behind file generated by the EF calls the ReportPropertyChanged function (as shown in this VB.Net snippet).

    Public Property firstName() As String
        Get
            Return Me._firstName
        End Get
        Set
            Me.OnfirstNameChanging(value)
            Me.ReportPropertyChanging("firstName")
            Me._firstName = Global.System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value, false)
            Me.ReportPropertyChanged("firstName")
            Me.OnfirstNameChanged
        End Set
    End Property

What about for a navigation property such as the "Role" property in our "User" entity.  First thing to note is that the EF code gen will actually create two properties for each navigation property in the model.  One has the same name as the navigation property itself, for example "Role", and the other is named with the word "Reference" appended to it, for example "RoleReference".  The first property can be used in your code to get/set the value of this property.  It is of the same type as the entity that the property refers to.  In the case of the "Role" property, you will get a Role returned.  The latter is actually an EntityReference object.  I am not really going to go into detail here as I am sure you can simply google "EntityReference" and find out all the information you will ever need to know.

Back to the problem!  If I set the "Role" property through my code, the PropertyChanged event will not get raised.  If we look at the generated code behind this function, we have something like this:

    Public Property Role() As Role
        Get
            Return CType(Me,Global.System.Data.Objects.DataClasses.IEntityWithRelationships).RelationshipManager.GetRelatedReference(Of Role)("MyDatabaseModel.FK_Users_Roles", "Roles").Value
        End Get
        Set
            CType(Me,Global.System.Data.Objects.DataClasses.IEntityWithRelationships).RelationshipManager.GetRelatedReference(Of Role)("MyDatabaseModel.FK_Users_Roles", "Roles").Value = value
        End Set
    End Property

You will notice that ReportPropertyChanged is not called here.  I am not too sure why Microsoft chose not to raise the event here, but I am sure they had their reasons.  Either way, this does not change the fact that I need to know when this property changes.

I hope that Microsoft will consider adding this functionality to the next release of the Entity Framework however, until then, here is a quick workaround.  It works great but at the same time, it's a real pity that this is necessary in the first place.


Workaround:

The EntityReference and EntityCollection classes both expose an “AssociationChanged” event.  You can listen to this event to see when the value in the related end changes. One thing you can do is to register an AssociationChanged event handler and have it call the INotifyPropertyChanged event.  To do this, you can take advantage of the fact that your code generated entities are partial classes and extend them with the registration of this handler.  Here is a quick C# Snippet (suppose the class names are User and Role, as above).

    public partial class User
    {
        private void RoleChanged(Object sender, CollectionChangeEventArgs e)
        {
            // Call the protected OnPropertyChanged method
            // which will fire the INotifyPropertyChanged.PropertyChanged event
            OnPropertyChanged("Role");
        }

        // Create a default constructor that registers your event handler
        public User()
        {
            this.RoleReference.AssociationChanged += RoleChanged;
        }
    }


 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this post.
Comments

  • August 17, 2010 4:01 AM lionel wrote:
    Yes man, you saved my time ! Thankls a lot for the workaround !
    Reply to this
  • August 17, 2010 4:46 AM lionel wrote:
    another comment : You got a leak in your workaround! You need to call this.RoleReference.AssociationChanged -= RoleChanged; somewhere...
    Where is the best place to call this "cleaning code" ?
    Reply to this
  • February 21, 2011 4:43 PM BlueRaja wrote:
    This can written more succinctly using C# 3.5 as follows:

    public User()
    {
    RoleReference.AssociationChanged += (s,e) => OnPropertyChanged("Role");
    }

    Helpful if you have many properties
    Reply to this
  • August 1, 2011 5:17 PM Hawkeye426 wrote:
    A better way of doing this, not requiring the use of a separate event handler is to create a partial OnPropertyChanged Method in a partial class of your entity object as follows:

    public partial class User
    {
    partial void OnRoleIDChanged()
    {
    //Call On Property Changed for
    //navigation property
    OnPropertyChanged("Role");
    }
    }
    Reply to this
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.