Track changes to collections using Entity Framework change tracker

c# entity-framework entity-framework-6

Question

For auditing/history purposes, I am using the Entity Framework change tracker to determine, before writing changes, what has changed and serialize the changes. I can get the changed entities by calling this.ChangeTracker.Entries() in my DbContext derivative and looking at the values for anything marked EntityState.Added, EntityState.Deleted, or EntityState.Modified. This all works great.

My problem is that this method does not work to track changes to collections of EF objects (for instance, an ICollection<Person> property on a PersonGroup object).

I'm sure the EF context must track this somehow -- how else would the database update work, after all? But is it available to me?

1
2
10/12/2015 8:59:30 PM

Accepted Answer

What you're looking for is relationship change tracking. You can find it in ObjectStateManager of the underlying ObjectContext, here is how you get all added relationships:

//you need to call DetectChanges
((IObjectContextAdapter)context).ObjectContext.DetectChanges();

var addedRelations = ((IObjectContextAdapter)context).ObjectContext
                              .ObjectStateManager.GetObjectStateEntries(EntityState.Added)
                                                 .Where(e=>e.IsRelationship).ToList();
3
10/12/2015 7:49:57 PM

Popular Answer

It turns out you can get at the relationships with this code (assuming it's running inside your DbContext derivative):

((IObjectContextAdapter) this).ObjectContext.ObjectStateManager
     .GetObjectStateEntries(EntityState.Added)
     .Where(e => e.IsRelationship)
     .Select(r => new {EntityKeyInfo = r.CurrentValues[0], 
                       CollectionMemberKeyInfo = r.CurrentValues[1], r.State});

Obviously you can tweak this based on what you need and it's up to do you something useful with it. The first two CurrentValues entries represent EntityKey objects which will allow you to get the IDs of the entities in question.

If you want to deal with deleted entities this won't work and you need to use reflection. Instead of CurrentValues[0] and CurrentValues[1] you can look at the internal properties Key0 and Key1, which are defined in an internal class you can't access at compile time. This will work: r.GetType().GetProperty("Key0", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(r, new object[0]). Note that this is probably not an intended use and could blow up whenever.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow