Entity Framework/SQL2008 - How to Automatically Update LastModified fields for Entities?
I know I'm a little late to the party, but I just solved this for a project I'm working on and thought I'd share my solution.
First, to make the solution more re-usable, I created a base class with the timestamp properties:
public class EntityBase
{
public DateTime? CreatedDate { get; set; }
public DateTime? LastModifiedDate { get; set; }
}
Then I overrode the SaveChanges method on my DbContext:
public class MyContext : DbContext
{
public override int SaveChanges()
{
ObjectContext context = ((IObjectContextAdapter)this).ObjectContext;
//Find all Entities that are Added/Modified that inherit from my EntityBase
IEnumerable<ObjectStateEntry> objectStateEntries =
from e in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
where
e.IsRelationship == false &&
e.Entity != null &&
typeof(EntityBase).IsAssignableFrom(e.Entity.GetType())
select e;
var currentTime = DateTime.Now;
foreach (var entry in objectStateEntries)
{
var entityBase = entry.Entity as EntityBase;
if (entry.State == EntityState.Added)
{
entityBase.CreatedDate = currentTime;
}
entityBase.LastModifiedDate = currentTime;
}
return base.SaveChanges();
}
}
As i have a service layer mediating between my controllers (im using ASP.NET MVC), and my repository, i have decided to auto-set the fields here.
Also, my POCO's have no relationships/abstractions, they are completely independant. I would like to keep it this way, and not mark any virtual properties, or create base classes.
So i created an interface, IAutoGenerateDateFields:
public interface IAutoGenerateDateFields
{
DateTime LastModified { get;set; }
DateTime CreatedOn { get;set; }
}
For any POCO's i wish to auto-generate these fields, i implement this inteface.
Using the example in my question:
public class PocoWithDates : IAutoGenerateDateFields
{
public string PocoName { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime LastModified { get; set; }
}
In my service layer, i now check if the concrete object implements the interface:
public void Add(SomePoco poco)
{
var autoDateFieldsPoco = poco as IAutoGenerateDateFields; // returns null if it's not.
if (autoDateFieldsPoco != null) // if it implements interface
{
autoDateFieldsPoco.LastModified = DateTime.Now;
autoDateFieldsPoco.CreatedOn = DateTime.Now;
}
// ..go on about other persistence work.
}
I will probably break that code in the Add out to a helper/extension method later on.
But i think this is a decent solution for my scenario, as i dont want to use virtuals on the Save (as i'm using Unit of Work, Repository, and Pure POCO's), and don't want to use triggers.
If you have any thoughts/suggestions, let me know.
I would also like to put forward a late solution. This one is only applicable to the .NET Framework 4 but makes this sort of task trivial.
var vs = oceContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var v in vs)
{
dynamic dv = v.Entity;
dv.DateLastEdit = DateTime.Now;
}
here is edited version of previous response. Previous one didn't work for updates for me.
public override int SaveChanges()
{
var objectStateEntries = ChangeTracker.Entries()
.Where(e => e.Entity is TrackedEntityBase && (e.State == EntityState.Modified || e.State == EntityState.Added)).ToList();
var currentTime = DateTime.UtcNow;
foreach (var entry in objectStateEntries)
{
var entityBase = entry.Entity as TrackedEntityBase;
if (entityBase == null) continue;
if (entry.State == EntityState.Added)
{
entityBase.CreatedDate = currentTime;
}
entityBase.LastModifiedDate = currentTime;
}
return base.SaveChanges();
}