Entity framework 'Code first' extented database models

If you use Entity framework Code first approach of database creation you should definetely read this article.
Personally, as a backend developer, I have created a lot of databases - at the begining for exersice, subsequently for real projects at work.
At some point the code first approach has proved itself as the best option when dealing with web development for a couple of reasons.
  • Firstly we may not be good at SQL at all, but still be able to create a decent database quickly and easily (which is highly inadvisable in my opinion, but it's possible) just by knowing the main concepts for the relationships between the tables (classes)
  • Secondly we can reuse our knowledge in OOP
  • Lastly the distribution is a lot easier using this approach (you don't have to script databases and etc.)

Now to the point!

If you have created code first databases more than a couple of times and you are good at object oriented principles you have to realize that there is something 'smelly' going on over there:
we have common properties between our models
So what do we do. Of course we are good at OOP so we create a Basic database model that will hold all of our common properties. But what should we put in it.
Here is a possible example:
 public abstract class BaseModel : IAuditInfo, IDeletableEntity
    {
        [Key]
        public TKey Id { get; set; }

        public DateTime CreatedOn { get; set; }

        public DateTime? ModifiedOn { get; set; }

        [Index]
        public bool IsDeleted { get; set; }

        public DateTime? DeletedOn { get; set; }
    }
And because we follow the OOP good practices we create our interfaces : IAuditInfo, IDeletableEntity

  public interface IAuditInfo
    {
        DateTime CreatedOn { get; set; }

        DateTime? ModifiedOn { get; set; }
    }
The IAuditInfo interface guarantees that all of our database records will be neatly accounted and we will know when each record has been created and modified.

  public interface IDeletableEntity
    {
        bool IsDeleted { get; set; }

        DateTime? DeletedOn { get; set; }
    }
The IDeletableEntity interface follows a practice that says that nothing should be deleted from the database, but only marked as deleted.

That's great, now all of our models will hold these common properties.

But something is missing

It's possible and this is how it's done
    public override int SaveChanges()
        {
            this.ApplyAuditInfoRules();
            return base.SaveChanges();
        }

        private void ApplyAuditInfoRules()
        {
            // Approach via @julielerman: http://bit.ly/123661P
            foreach (var entry in
                this.ChangeTracker.Entries()
                    .Where(
                        e =>
                        e.Entity is IAuditInfo && ((e.State == EntityState.Added) || (e.State == EntityState.Modified))))
            {
                var entity = (IAuditInfo)entry.Entity;
                if (entry.State == EntityState.Added && entity.CreatedOn == default(DateTime))
                {
                    entity.CreatedOn = DateTime.UtcNow;
                }
                else
                {
                    entity.ModifiedOn = DateTime.UtcNow;
                }
            }
        }
This block of code should be put in your database context class and it maintans the IAuditInfo interface properties itself.
Now you probably think:
but wait...how about these things for the 'not deleting anything practice', we still have to filter all the deleted things whenever we make query to the database?
And you would be absolutely right, to ask this! But there is a solution to this also.
And the solution is to implement a Repository pattern, like an abstraction level over the database. Here is an example:
    public class DbRepository : IDbRepository
        where T : BaseModel
    {
        public DbRepository(DbContext context)
        {
            if (context == null)
            {
                throw new ArgumentException("An instance of DbContext is required to use this repository.", nameof(context));
            }

            this.Context = context;
            this.DbSet = this.Context.Set();
        }

        private IDbSet DbSet { get; }

        private DbContext Context { get; }

        public IQueryable All()
        {
            return this.DbSet.Where(x => !x.IsDeleted);
        }


        public virtual IQueryable All(string[] includes)
        {
            var data = this.DbSet.AsQueryable();
            for (int i = 0; i < includes.Length; i++)
            {
                data = data.Include(includes[i]);
            }

            return data;
        }

        public IQueryable AllWithDeleted()
        {
            return this.DbSet;
        }

        public T GetById(int id)
        {
            return this.All().FirstOrDefault(x => x.Id == id);
        }

        public void Add(T entity)
        {
            this.DbSet.Add(entity);
        }

        public void Delete(T entity)
        {
            entity.IsDeleted = true;
            entity.DeletedOn = DateTime.UtcNow;
        }

        public void HardDelete(T entity)
        {
            this.DbSet.Remove(entity);
        }

        public void Save()
        {
            this.Context.SaveChanges();
        }


    } 
This way when we make a query to the database we can easily filter all the deleted items, and that wouldn't affect us at all.
Moreover we can make a delete method that do all the requeired steps to make database entity to be 'marked as deleted'.