How to automatically set CreatedOn fields with Entity Framework on saved entity and its child properties

c# entity-framework entity-framework-6 inheritance

Question

Whenever I call databaseContext.SaveChanges(), before data is saved by Entity Framework, I need all objects and their child classes to have the CreatedOn field populated with DateTime.Now().

Details: I have a BaseEntity class that all entities inherit from:

 public class BaseEntity
 {
        [Key]
        public virtual Guid Id{ get; set; }
        public DateTime CreatedOn { get; set; }
 }

Whenever an entity that inherits from BaseEntity above is saved, the CreatedOn property gets assigned DateTime.Now.

Here is a base repository that does it:

public abstract class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : BaseEntity
{
     ....

     public virtual bool SaveChanges(TEntity entity)
     {            
         if (entity.CreatedOn == DateTime.MinValue)
             entity.CreatedOn = DateTime.Now;
         else
             entity.ModifiedOn = DateTime.Now;

         return _databaseContext.SaveChanges() > 0;           
     }
}

This works well but the problem is that only object's itself CreatedOn property gets updated. Any child classes there may be do not get updated.

How can I change my logic in the SaveChanges() method to update all child classes and set their CreatedOn dates as well?

I'll provide an example to make this more clear: imagine an instance of a User object below added to the dbContext with a new Profile, as well as new Role class instances assigned and then SaveChanges() is called:

public class User: BaseEntity
{
        [Key]
        public virtual Guid Id { get; set; }

        public UserRole Role { get; set; }
        public ProfileDetails Profile { get; set; }
        public DateTime CreatedOn { get; set; }
        public Guid CreatedBy { get; set; }
}

What's the best way to make sure that CreatedOn date gets assigned for child user.Role, as well as user.Profile objects (both classes also inherit from BaseEntity)? I thought of using Reflection to check child object properties for CreatedOn fields but all the looping doesn't feel right. Is there a better way?

1
1
3/3/2020 10:40:13 PM

Accepted Answer

Basically, you should override the SaveChanges method in your own DB context class and use the EF change tracker to get all newly created objects, and then set their CreatedOn fields accordingly.

Something along the lines of this:

public class DbContextBase : DbContext 
{
    public override int SaveChanges() 
    {
        DateTime currentDateTime = DateTime.Now;

        // get all the entities in the change tracker - this could be optimized
        // to fetch only the entities with "State == added" if that's the only 
        // case you want to handle
        IEnumerable<DbEntityEntry<BaseEntity>> entities = ChangeTracker.Entries<BaseEntity>();

        // handle newly added entities
        foreach (DbEntityEntry<BaseEntity> entity in entities.Where(e => (e.State == EntityState.Added)) 
        {
            // set the CreatedOn field to the current date&time
            entity.Entity.CreatedOn = currentDateTime;
        }

        // to the actual saving of the data
        return base.SaveChanges();
    }
}

Of course you could improve on this:

  • also handle the entities with e.State == EntityState.Modified and set a ModifiedOn field in this case
  • add some custom exception handling to handle common problem cases
  • so much more - the sky and your imagination is the limit!
1
2/2/2020 7:52:56 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow