Overriding SaveChanges in Entity Framework

Introduction

In Entity Framework starting from version 4 and above we have the option to override the default “SaveChanges()” method and use some custom code or some business logic before committing the changes to the database. And this feature is very handy when we have entities with common properties that need to be handled automatically. We have encountered a few scenarios where we have entities with a modified date and modified by the user property that needs to be be updated correctly each time we deal with those entities. So rather than assigning a value to those properties every time throughout the application, what we can do is to have a single place with some logic written for the entities to populate the modified date and modified user name and than we can save the changes to the database. The scenario that I am talking about can be easily handled by overriding the “SaveChanges()” method of Entity Framework. Let's understand it with an example below.

Explanation

Let us say we have three entities named Employee, Department and Employment and they look something like:

  1. public class Department : EntityInformation  
  2.    {  
  3.        public int DepartmentID { getset; }  
  4.        public string DepartmentName { getset; }  
  5.        public string Location { getset; }  
  6.   
  7.        public virtual ICollection<Employment> Employments { getset; }  
  8.    }  
  9.   
  10. public class Employee : EntityInformation  
  11.    {  
  12.        public int EmployeeID { getset; }  
  13.        public string Name { getset; }  
  14.        public int Age { getset; }  
  15.   
  16.        public virtual ICollection<Employment> Employments { getset; }  
  17.    }  
  18.   
  19. public class Employment   
  20.    {  
  21.        public int EmploymentID { getset; }  
  22.        public int EmployeeID { getset; }  
  23.        public int DepartmentID { getset; }  
  24.   
  25.        public virtual Department Department { getset; }  
  26.        public virtual Employee Employee { getset; }  
  27.    }      
And the common entity:
  1. public class EntityInformation  
  2.     {  
  3.         public DateTime ModifiedDate { getset; }  
  4.         public string ModifiedUserName { getset; }  
  5.     }  
So both Employee and Department have the common property “ModifiedDate” and “ModifiedUserName”. Depending on the business we need to update the modified date and the user name each time we deal with these two entities. Either we are creating a new employee or department or we are updating an existing employee or department. In both cases we need to populate the current date in the “ModifiedDate” column and the current user in the “ModifiedUserName” column.

As stated earlier, here we will override the default “SaveChanges()” method of Entity Framework. Since we are overriding “SaveChanges()” our context class will look as in the following.
  1. public class OrganizationContext : DbContext  
  2.     {  
  3.         public OrganizationContext()  
  4.             : base("OrganizationContext")  
  5.         {  
  6.         }  
  7.   
  8.         public DbSet<Employee> Employees { getset; }  
  9.         public DbSet<Department> Departments { getset; }  
  10.         public DbSet<Employment> Employments { getset; }  
  11.   
  12.         protected override void OnModelCreating(DbModelBuilder modelBuilder)  
  13.         {  
  14.             modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();  
  15.         }  
  16.   
  17.         /// <summary>  
  18.         /// Overriding Save Changes  
  19.         /// </summary>  
  20.         /// <returns></returns>  
  21.         public override int SaveChanges()  
  22.         {  
  23.             var selectedEntityList = ChangeTracker.Entries()  
  24.                                     .Where(x => x.Entity is EntityInformation &&  
  25.                                     (x.State == EntityState.Added || x.State == EntityState.Modified));  
  26.   
  27.             //Gt user Name from  session or other authentication   
  28.             var userName = "MUKESH";  
  29.   
  30.             foreach (var entity in selectedEntityList)  
  31.             {  
  32.   
  33.                 ((EntityInformation)entity.Entity).ModifiedDate = DateTime.Now;  
  34.                 ((EntityInformation)entity.Entity).ModifiedUserName = userName;  
  35.             }  
  36.   
  37.             return base.SaveChanges();  
  38.         }  
  39.     }  
Now let's analyze this “SaveChanges()” method.
  1. var selectedEntityList = ChangeTracker.Entries()  
  2. .Where(x => x.Entity is EntityInformation &&  
  3. (x.State == EntityState.Added || x.State == EntityState.Modified));  
We know that the context monitors the changed entities in the connection and we can get all the tracked entities using “ChangeTracker.Entities()”. Now there can be n number of entities and all the entities will not necessarily have the common properties “ModifiedDate” and “ModifiedUserName” so we need to find the entities with the common properties, in other words we need to get the entities of type “EntityInformation”. And in the last segment we are trying to get the entities that are in the modified and added state, there are other entity states also (deleted, detached and unchanged) but we are mainly concerned with these two (modified and added) entity states.
  1. foreach (var entity in selectedEntityList)  
  2.             {  
  3.   
  4.                 ((EntityInformation)entity.Entity).ModifiedDate = DateTime.Now;  
  5.                 ((EntityInformation)entity.Entity).ModifiedUserName = userName;  
  6.             }  
Now in the code above we are looping through all the tracked entities of type “EntityInformation” with the entity state modified or added. And for each entity we are assigning “ModifiedDate” and “ModifiedUserName”.
  1. return base.SaveChanges();  
And finally calling the “SaveChanges()” method of the base class.

So now whenever you call “SaveChanges()” with the entities of type “EntityInformation” the “ModifiedDate” and “ModifiedUserName” will be handled automatically and will be populated for that entity.

I hope this will help!