Entity Framework: Tracking changes to FK associations

I'm overriding SaveChanges on my DbContext in order to implement an audit log. Working with many-to-many relationships or independent associations is relatively easy as EF creates ObjectStateEntries for any changes to those kinds of relationships.

I am using foreign key associations, and when a relationship between entities changes all you get is an ObjectStateEnty that says for example entity "Title" has "PublisherID" property changed. To a human this is obviously a foreign key in Title entity, but how do I determine this in runtime? Is there a way to translate this change to a "PublisherID" property to let's an EntityKey for the entity that foreign key represents?

I assume I'm dealing with entities that look like this:

public sealed class Publisher
    public Guid ID { get; set; }
    public string Name { get; set; }
    public ICollection<Title> Titles { get; set; }

public class Title
    public Guid ID { get; set; }
    public string Name { get; set; }
    public Guid? PublisherID { get; set; }
    public Publisher Publisher { get; set; }

There is also EF EntityConfiguration code that defines the relationship and foreign key:

public TitleConfiguration()
    HasOptional<Publisher>(t => t.Publisher).WithMany(
            p => p.Titles).HasForeignKey(t => t.PublisherID);

What I'm doing now seems a bit too complicated. I'm hoping there is more elegant way to achieve my goal. For every modified property from ObjectStateEntry I look through all ReferentialConstraints for current entity and see if any of those use it as a foreign key. The code below is called from SaveChanges():

private void HandleProperties(ObjectStateEntry entry, 
        ObjectContext ctx)
    string[] changedProperties = entry.GetModifiedProperties().ToArray();
    foreach (string propertyName in changedProperties)
        HandleForeignKey(entry, ctx, propertyName);

private void HandleForeignKey(ObjectStateEntry entry, 
        ObjectContext ctx, string propertyName)
    IEnumerable<IRelatedEnd> relatedEnds = 

    foreach (IRelatedEnd end in relatedEnds)
        // find foreign key relationships
        AssociationType elementType = end.RelationshipSet.ElementType as 
        if (elementType == null || !elementType.IsForeignKey) continue;

        foreach (ReferentialConstraint constraint in 
            // Multiplicity many means we are looking at a foreign key in a 
            // dependent entity
            // I assume that ToRole will point to a dependent entity, don't 
            // know if it can be FromRole
            Debug.Assert(constraint.ToRole.RelationshipMultiplicity == 
            // If not 1 then it is a composite key I guess. 
            // Becomes a lot more difficult to handle.
            Debug.Assert(constraint.ToProperties.Count == 1);
            EdmProperty prop = constraint.ToProperties[0];

            // entity types of current entity and foreign key entity 
            // must be the same
            if (prop.DeclaringType == entry.EntitySet.ElementType 
                    && propertyName == prop.Name)
                EntityReference principalEntity = end as EntityReference;
                if (principalEntity == null) continue;

                EntityKey newEntity = principalEntity.EntityKey;
                // if there is more than one, the foreign key is composite
                Debug.Assert(newEntity.EntityKeyValues.Length == 1);

                // create an EntityKey for the old foreign key value
                EntityKey oldEntity = null;

                if (entry.OriginalValues[prop.Name] is DBNull)
                    oldEntity = new EntityKey();
                    oldEntity.EntityKeyValues = new[] { 
                        new EntityKeyMember("ID", "NULL") 
                    oldEntity.EntitySetName = newEntity.EntitySetName;
                    Guid oldGuid = Guid.Parse(
                    oldEntity = ctx.CreateEntityKey(newEntity.EntitySetName, 
                            new Publisher()
                                ID = oldGuid

                        "Foreign key {0} changed from [{1}: {2}] to [{3}: {4}]", 
                        oldEntity.EntitySetName, oldEntity.EntityKeyValues[0],
                        newEntity.EntitySetName, newEntity.EntityKeyValues[0]);

I hope this helps to illustrate better what I am trying to achieve. Any input is welcome.



Looks like my code is the right solution to this problem :/

I did end up using independent associations to avoid this problem altogether.

Need Your Help

Binding data in created dropdown from ajax data

c# javascript .net ajax asp.net-mvc-4

I have following dropdown created in cshtml page: