Entity framework 'Code first' extented database models

 If you use Entity framework Code first approach of database generation 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
 I have been working on projects in which we create different properties like CreatedOn, Date, DateCreated and etc. for each different database model. If you are the only one who is doing the database structure, you probably fallow some convention, but when more people are involved and there isn't any known convention it gets messy at some point.
 Here is the first place where a red light should stop you from goind further.
...
Because of the fallowing reasons:
  • There is duplication in our code
  • We use properties called differently for semantically equal entities
  • We generate more work when handling each object separatly
  • We are not being consistent in our database naming and structure, now some of the tables will have information about the creation of the records and some will not

 So what do we do. We will use our skills in OOP and we will 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'.