18

Many of the tables in my database need to have a "DateCreated" and "DateModified" column. I want to update these columns whenever SaveChanges() is called.

All my model objects inherit from a class AbstractModel to allow this kind of behavior. The class looks like this:

public abstract class AbstractModel {
    public virtual void UpdateDates() { }
}

I then plan to override UpdateDates() in any child classes that have DateCreated and DateModified fields they need to maintain.

To use this, I need to be able to get a list of all the entities that the DbContext is tracking, and call UpdateDates() on them if the object is marked as being Added or Modified.

I can't seem to get access to wherever DbContext is storing this information. I can do dbContext.Entry(object).State to get the EntityState of a single entity, but I can't work out how to get a list of all tracked entities.

How do I do this?

Oliver
  • 11,297
  • 18
  • 71
  • 121

3 Answers3

33

I think you can use the ChangeTracker for this:

public class MyContext : DbContext
{
    //...

    public override int SaveChanges()
    {
        foreach (var dbEntityEntry in ChangeTracker.Entries<AbstractModel>())
        {
            dbEntityEntry.Entity.UpdateDates();
        }
        return base.SaveChanges();

    }
}
nemesv
  • 138,284
  • 16
  • 416
  • 359
9

The accepted solution here is better for above EF4 than the ObjectContext suggestion - we ran into issues with for instance the in memory db we used in the test context not working well with the above solution (getting Detached state for some modified entries in inheritance scenarios). This solution uses the DbContext native way of retrieving the tracked entities:

context.ChangeTracker.Entries()
        .Where (t => t.State == EntityState.Modified)
Community
  • 1
  • 1
Arwin
  • 973
  • 1
  • 10
  • 21
2

This has worked well in our project so we don't have to remember to override something every time we add a new entity that has our standard LastUpdatedDateTime property. Using EF4; for an EF5 DbContext I think ((IObjectContextAdapter)_dbContext).ObjectContext.ObjectStateManager would get you the object state manager:

// In our SaveChanges wrapper:
        var entries = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Deleted);

        private static void PopulateDate(IEnumerable<ObjectStateEntry> entries)
                {
                    foreach (var entry in entries)
                    {
                        if (entry.State != EntityState.Deleted)
                        {
                            if ((entry.Entity != null) && (entry.Entity.GetType().GetProperty("LastUpdatedDateTime") != null))
                            {
                                ((dynamic)entry.Entity).LastUpdatedDateTime = DateTime.UtcNow;
                            }
                        }
                    }
                }

The reflection call is not an issue: for an object with 25 properties (alot), a million reflection queries takes an avg ~210 ms.

dudeNumber4
  • 4,217
  • 7
  • 40
  • 57