/// <summary>
        ///     Finds the by.
        /// </summary>
        /// <param name="where">The where.</param>
        /// <returns></returns>
        public virtual IEnumerable <TEntity> FindBy(Expression <Func <TEntity, bool> > where)
        {
            IEnumerable <TEntity> query;

            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                var dbSet = CurrentDbContext.Set <TEntity>();
                query = dbSet.Where(where).ToList();
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }

            return(query);
        }
        /// <summary>
        ///     Finds the by asynchronous.
        /// </summary>
        /// <param name="where">The where.</param>
        /// <param name="include">The include.</param>
        /// <returns></returns>
        public virtual async Task <IEnumerable <TEntity> > FindByAsync(Expression <Func <TEntity, bool> > where,
                                                                       Expression <Func <TEntity, object> > include)
        {
            IEnumerable <TEntity> result;

            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                var dbSet = CurrentDbContext.Set <TEntity>();
                result = await dbSet.Where(where).Include(include).ToListAsync();
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }

            return(result);
        }
        /// <summary>
        ///     Initializes a new instance of the <see cref="GenericRepository{TEntity}" /> class.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <exception cref="ArgumentNullException"></exception>
        public GenericRepository(DbContext context)
        {
            Context = context;

            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                CurrentDbSet = CurrentDbContext.Set <TEntity>();
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }
        }
        /// <summary>
        ///     Attaches the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Attach(TEntity entity)
        {
            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                var dbset = CurrentDbContext.Set <TEntity>();

                dbset?.Attach(entity);
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }
        }
        /// <summary>
        ///     Gets all.
        /// </summary>
        /// <param name="navigationProperties">The navigation properties.</param>
        /// <returns></returns>
        public virtual IList <TEntity> GetAll(params Expression <Func <TEntity, object> >[] navigationProperties)
        {
            //usage example:
            //IGenericDataRepository<Department> repository = new GenericDataRepository<Department>();
            //IList<Department> departments = repository.GetAll(d => d.Employees);

            List <TEntity> list;

            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                IQueryable <TEntity> dbSet = CurrentDbContext.Set <TEntity>();

                if (navigationProperties != null)
                {
                    //Apply eager loading
                    dbSet = navigationProperties.Aggregate(
                        dbSet,
                        (current, navigationProperty) => current.Include(navigationProperty)
                        );
                }

                if (dbSet == null)
                {
                    return(null);
                }

                list = dbSet
                       .AsNoTracking()
                       .ToList();
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }

            return(list);
        }
        /// <summary>
        ///     GetListAsync
        /// </summary>
        /// <param name="where">The where.</param>
        /// <param name="navigationProperties">The navigation properties.</param>
        /// <returns></returns>
        public virtual async Task <List <TEntity> > GetListAsync(Func <TEntity, bool> where,
                                                                 params Expression <Func <TEntity, object> >[] navigationProperties)
        {
            List <TEntity> list;

            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                IQueryable <TEntity> dbSet = CurrentDbContext.Set <TEntity>();

                if (navigationProperties != null)
                {
                    //Apply eager loading
                    dbSet = navigationProperties.Aggregate(
                        dbSet,
                        (current, navigationProperty) => current.Include(navigationProperty)
                        );
                }

                if (dbSet == null)
                {
                    return(null);
                }

                list = await dbSet
                       .AsNoTracking()
                       .Where(where)
                       .AsQueryable()
                       .ToListAsync();
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }

            return(list);
        }
        /// <summary>
        ///     Gets the single asynchronous.
        /// </summary>
        /// <param name="where">The where.</param>
        /// <param name="navigationProperties">The navigation properties.</param>
        /// <returns></returns>
        public virtual async Task <TEntity> GetSingleAsync(Func <TEntity, bool> where,
                                                           params Expression <Func <TEntity, object> >[] navigationProperties)
        {
            TEntity item;

            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                IQueryable <TEntity> dbSet = CurrentDbContext.Set <TEntity>();

                if (navigationProperties != null)
                {
                    //Apply eager loading
                    dbSet = navigationProperties.Aggregate(
                        dbSet,
                        (current, navigationProperty) => current.Include(navigationProperty)
                        );
                }

                if (dbSet == null)
                {
                    return(null);
                }

                item = await dbSet
                       .AsNoTracking()                             //Don't track any changes for the selected item
                       .FirstOrDefaultAsync(w => w.Equals(where)); //Apply where clause
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }

            return(item);
        }
        /// <summary>
        ///     Edits the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public virtual void Edit(TEntity entity)
        {
            AppUtility.ValidateContext(CurrentDbContext);

            AppUtility.ValidateEntity(entity);

            //this sets base value
            UpdatedOn = DateTime.UtcNow;
            //this sets model value
            entity.UpdatedOn = UpdatedOn ?? DateTime.UtcNow;

            EntityState = EntityState.Modified;
            CurrentDbContext.Entry(entity).State = EntityState.Modified;
        }
        /// <summary>
        ///     Updates the many.
        /// </summary>
        /// <param name="items">The items.</param>
        public virtual void UpdateMany(params TEntity[] items)
        {
            AppUtility.ValidateContext(CurrentDbContext);

            if (items == null)
            {
                return;
            }

            try
            {
                foreach (var item in items)
                {
                    //this sets base value
                    UpdatedOn = DateTime.UtcNow;
                    //this sets model value
                    item.UpdatedOn = UpdatedOn ?? DateTime.UtcNow;

                    EntityState = EntityState.Modified;
                    CurrentDbContext.Entry(item).State = EntityState.Modified;
                }
                CurrentDbContext.SaveChanges();
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }
        }
        /// <summary>
        ///     GetAll async
        /// </summary>
        /// <returns></returns>
        public virtual async Task <List <TEntity> > GetAllAsync()
        {
            List <TEntity> list;

            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                IQueryable <TEntity> dbSet = CurrentDbContext.Set <TEntity>();

                if (dbSet == null)
                {
                    return(null);
                }

                //Apply eager loading, async
                list = await dbSet
                       .AsNoTracking()
                       .ToListAsync();
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }

            return(list);
        }
        /// <summary>
        ///     Gets all.
        /// </summary>
        /// <returns></returns>
        public virtual IEnumerable <TEntity> GetAll()
        {
            List <TEntity> list;

            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                IQueryable <TEntity> dbSet = CurrentDbContext.Set <TEntity>();

                if (dbSet == null)
                {
                    return(null);
                }

                list = dbSet
                       .AsNoTracking()
                       .ToList();
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }

            return(list);
        }
        /// <summary>
        ///     Saves this instance.
        /// </summary>
        /// <returns></returns>
        public virtual int Save()
        {
            var result = -1;

            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                result = CurrentDbContext.SaveChanges();

                Audit.Log.Info(string.Format("Repository Save() :: result: {0}", result));
            }
            catch (DbEntityValidationException ex)
            {
                var errors = ex.EntityValidationErrors.SelectMany(
                    x => x.ValidationErrors.Select(y => new ValidationResult(y.ErrorMessage, new[] { y.PropertyName })));

                Audit.Log.Error(
                    string.Format("{0} :: {1} ", AppConstants.ErrorMessages.DbEntityValidationExceptionMessage, errors),
                    ex);
            }
            catch (DbUpdateException ex)
            {
                var decodedErrors = TryDecodeDbUpdateException(ex);
                if (decodedErrors == null)
                {
                    throw; //it isn't something we understand
                }
                Audit.Log.Error(
                    string.Format("{0} :: {1} ", AppConstants.ErrorMessages.DbExceptionMessage, decodedErrors), ex);
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }

            return(result);
        }
        /// <summary>
        ///     Finds the by.
        /// </summary>
        /// <param name="where">The where.</param>
        /// <param name="include">The include.</param>
        /// <returns></returns>
        public virtual IEnumerable <TEntity> FindBy(Expression <Func <TEntity, bool> > @where,
                                                    Expression <Func <TEntity, object> > @include)
        {
            var result = default(IEnumerable <TEntity>);

            AppUtility.ValidateContext(CurrentDbContext);

            try
            {
                var dbQuery = CurrentDbContext.Set <TEntity>();

                if (dbQuery != null)
                {
                    result = dbQuery.Where(where).Include(include).ToList();
                }
            }
            catch (SqlException sqex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.SqlExceptionMessage, sqex);

                throw;
            }
            catch (DbException dbex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.DbExceptionMessage, dbex);

                throw;
            }
            catch (Exception ex)
            {
                Audit.Log.Error(AppConstants.ErrorMessages.ExceptionMessage, ex);

                throw;
            }

            return(result);
        }