Automatic created and updated dates with Entity Framework

If you speak to any DBA (obviously only when you have to) the one thing they all appreciate is having updated and created dates on your database tables. They will try and push you down some Sql trigger route or something similar, but we are developers. We can't test that!

I have seen some odd implementations of this in Entity Framework (other ORM's are available). Including the use of base classes and setting the date in the constructor (eugh!). Doing it with base classes means you can't inherit your entities from anything else.

The way I do it follows the SOLID priciples (which I'm sure I'll reference more than once on this site) and is a much neater way to do it. It involves creating an interface for both the updated and created dates. Then you can add this to your entities.

So, for the created date we need to following:

public interface IDateCreated
{
	DateTime DateCreated { get; set; }
}

Then we need one for the updated date. Note that this is a nullable date. If an entity is never updated, then we need never set the date.

public interface IDateUpdated
{
	DateTime? DateUpdated { get; set; }
}

To complete this, then I create a placeholder interface for both. If a lot of tables with need both, we can use this:

public interface IDateCreatedAndUpdated : IDateCreated, IDateUpdated
{
}

We can now add this to our entities so they are ready to use. No base classes, just interfaces:

public class MyEntity : IDateCreatedAndUpdated
{
	public int Id { get; set; }
	public DateTime DateCreated { get; set; }
	public DateTime? DateUpdated { get; set; }
}

Now for the Entity Framework Magic. We need a method in our DbContext which will update these properties.

The first part will look for any entities in the change tracker which have a status of 'Added'.

The second will do the same for any which have a status of  'Updated' and set that date.

private void SetProperties()
{
    foreach (var entity in ChangeTracker.Entries().Where(p => p.State == EntityState.Added))
    {
		var created = entity.Entity as IDateCreated;
		if (created != null)
		{
			created.DateCreated = DateTime.Now;
		}
    }
    
    foreach (var entity in ChangeTracker.Entries().Where(p=>p.State == EntityState.Modified))
    {
    	var updated = entity.Entity as IDateUpdated;
    	if (updated != null)
    	{
    		updated.DateUpdated = DateTime.Now;
    	}
    }
}

Putting this all together, we need to call this method on save. Because we are good little developers, we override the standard save and both async save methods.

public class MyDbContext : DbContext
{
	public DbSet MyEntities { get; set; }

	public override int SaveChanges()
	{
		SetProperties();
		return base.SaveChanges();
	}

	public override Task SaveChangesAsync()
	{
		SetProperties();
		return base.SaveChangesAsync();
	}

	public override Task SaveChangesAsync(CancellationToken cancellationToken)
	{
		SetProperties();
		return base.SaveChangesAsync(cancellationToken);
	}

	private void SetProperties()
	{
		foreach (var entity in ChangeTracker.Entries().Where(p => p.State == EntityState.Added))
		{
			var created = entity.Entity as IDateCreated;
			if (created != null)
			{
				created.DateCreated = DateTime.Now;
			}
		}

		foreach (var entity in ChangeTracker.Entries().Where(p=>p.State == EntityState.Modified))
		{
			var updated = entity.Entity as IDateUpdated;
			if (updated != null)
			{
				updated.DateUpdated = DateTime.Now;
			}
		}
	}
}

This now means whenever we save through entity framework, it will set the relevant dates for us. Leaving us with a happy (for now) DBA.

One point to note, I don't access DateTime.Now in my actual implementation. This relies on a static method which is bad for unit testing. This is something I will cover in a later post.