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?
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:
e.State == EntityState.Modified
and set a ModifiedOn
field in this case