Making SaveChanges Work for You





5.00/5 (2 votes)
How to make SaveChanges work for you
Ok, so this update is going to be a short one, mainly because I am in the process of researching for a longer blog post on Microsoft Azure, and a process article on implementing scrum into a young development team.
But those are topics for future articles, and will be released shortly. I wanted to take a second to talk about Entity Framework. Entity Framework is a powerful tool, for creating and working with a data access layer of an application. It allows developers to quickly and efficiently craft a data layer, and work with it through the use of tools like LINQ.
In my experience though, Entity Framework does have one drawback - it does its job a little too well. It makes things a little too easy on many developers, and as a result, this tool tends to be under-utilized. In my experience, developers tend to create their database, drop it into Entity Framework, and then start building a "Data" layer on top of it which essentially is completely counter-productive.
One perfect example is the idea of "Audit
" fields. Many of us have built the same audit fields into every table. Things like "DateAdded
", "AddedBy
", "DateModified
", and "ModifiedBy
". We build fields like these are standard practice to help identify and track data changes. This can definitely help to keep data integrity.
I can't tell you how many times I've worked on applications, where I see things like repositories that are built, on top of entity framework... say that there are 10 repositories, and every single one of them has an "Insert
" method, and they all have logic to populate the "DateAdded
" and "AddedBy
".
Instead, what I'm advising is that "Entity Framework" doesn't have to be a "black box". A good example is the use of these audit fields, and utilize "SaveChanges
".
By overriding "SaveChanges
", we have the ability to build this auditing functionality directly into entity framework.
The following code is from a personal project of mine. And it outlines the process of leveraging entity framework to provide a single point for this operation instead of replicating it everywhere.
By utilizing an interface like the following:
public interface IEntity
{
DateTime CreatedDate { get; set; }
string CreatedBy { get; set; }
DateTime? ModifiedDate { get; set; }
string ModifiedBy { get; set; }
}
By implementing the above interface, on all data tables that have those fields. It will then make it easier to identify them whenever needed.
public partial class TaskEntities
{
public override int SaveChanges()
{
var auditable = ChangeTracker.Entries<IEntity>().ToList();
if (!auditable.Any()) return base.SaveChanges();
foreach (var dbEntry in auditable)
{
switch (dbEntry.State)
{
case System.Data.Entity.EntityState.Added:
dbEntry.Entity.CreatedDate = DateTime.Now;
dbEntry.Entity.CreatedBy = HttpContext.Current.User.Identity.Name;
break;
case System.Data.Entity.EntityState.Modified:
if (String.IsNullOrEmpty(dbEntry.Entity.CreatedBy))
{
dbEntry.Entity.CreatedDate = DateTime.Now;
dbEntry.Entity.CreatedBy = HttpContext.Current.User.Identity.Name;
}
dbEntry.Entity.ModifiedDate = DateTime.Now;
dbEntry.Entity.ModifiedBy = HttpContext.Current.User.Identity.Name;
break;
}
}
return base.SaveChanges();
}
}
In the above code, the web application checks all objects that are being saved and if they implement the IEntity
interface, it populates the appropriate fields. What this does is guarantee that the data layer handles this type of operation and prevents the developers from having to build extra logic to accomodate this requirement. It also ensures that any new tables added can have this logic immediately added without extra knowledge or work on behalf of the developer.
This is only a single example of how we can extend Entity Framework further to better accomodate this type of work, without building redundant data layers on top of it.