Tuesday, 29 November 2016

How can I create a generic data repository for my Entity Framework solution?

I recently created a quick console application that required CRUD activities on a SQL database. I had a few tables to update and did not want the headache of creating multiple items in my DAL.

Enter a generic data repository.

The code is pretty simple:

1. Create a base interface for the CRUD operations

public interface IGenericDataRepository<T> where T : class
    {
        List<T> GetAll(params Expression<Func<T, object>>[] navigationProperties);
        List<T> GetList(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties);
        T GetSingle(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties);
        void Add(params T[] items);
        void Update(params T[] items);
        void Remove(params T[] items);
        void RemoveAll(string tableName);
        void ExecuteSQLCommand(string sqlCommand, SqlParameter[] parameters);
    }

2. Create your Entity Framework data model. I called my ImportEntities.

3. Create a repository class that uses the interface

 public class GenericDataRepository<T> : IGenericDataRepository<T> where T : class
    {
        public virtual List<T> GetAll(params Expression<Func<T, object>>[] navigationProperties)
        {
            List<T> list;
            using (var context = new ImportEntities())
            {
                IQueryable<T> dbQuery = context.Set<T>();

                //Apply eager loading
                foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
                    dbQuery = dbQuery.Include<T, object>(navigationProperty);

                list = dbQuery
                    .AsNoTracking()
                    .ToList<T>();
            }
            return list;
        }

        public virtual List<T> GetList(Func<T, bool> where,
             params Expression<Func<T, object>>[] navigationProperties)
        {
            List<T> list;
            using (var context = new ImportEntities())
            {
                IQueryable<T> dbQuery = context.Set<T>();

                //Apply eager loading
                foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
                    dbQuery = dbQuery.Include<T, object>(navigationProperty);

                list = dbQuery
                    .AsNoTracking()
                    .Where(where)
                    .ToList<T>();
            }
            return list;
        }

        public virtual T GetSingle(Func<T, bool> where,
             params Expression<Func<T, object>>[] navigationProperties)
        {
            T item = null;
            using (var context = new ImportEntities())
            {
                IQueryable<T> dbQuery = context.Set<T>();

                //Apply eager loading
                foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
                    dbQuery = dbQuery.Include<T, object>(navigationProperty);

                item = dbQuery
                    .AsNoTracking() //Don't track any changes for the selected item
                    .FirstOrDefault(where); //Apply where clause
            }
            return item;
        }

        public virtual void Add(params T[] items)
        {
            try
            {
                using (var context = new ImportEntities())
                {
                    foreach (T item in items)
                    {
                        context.Entry(item).State = EntityState.Added;
                    }
                    context.SaveChanges();
                }
            }
            catch (DbEntityValidationException dbEx)
            {
                foreach (var validationErrors in dbEx.EntityValidationErrors)
                {
                    foreach (var validationError in validationErrors.ValidationErrors)
                    {
                        Console.WriteLine("Property: {0} Error: {1}",
                                                validationError.PropertyName,
                                                validationError.ErrorMessage);
                    }
                    Console.ReadLine();
                }
            }

        }

        public virtual void Update(params T[] items)
        {
            using (var context = new ImportEntities())
            {
                foreach (T item in items)
                {
                    context.Entry(item).State = EntityState.Modified;
                }
                context.SaveChanges();
            }
        }

        public virtual void Remove(params T[] items)
        {
            using (var context = new ImportEntities())
            {
                foreach (T item in items)
                {
                    context.Entry(item).State = EntityState.Deleted;
                }
                context.SaveChanges();
            }
        }

        public virtual void RemoveAll(string tableName)
        {
            using (var context = new ImportEntities())
            {
                context.Database.ExecuteSqlCommand(string.Format("TRUNCATE TABLE [{0}]", tableName));
            }
        }

        public virtual void ExecuteSQLCommand(string sqlCommand, SqlParameter[] parameters)
        {
            using (var context = new ImportEntities())
            {
                context.Database.ExecuteSqlCommand(sqlCommand, parameters);
            }
        }
    }

4. Now, we can access the repository in code: For example, I have a table called 'TempTable' configured with my Entity Framework data model, I can then instantiate the repository like so:

var repository = new GenericDataRepository<TempTable>();

and use it as follows:
- clear the list: 
repository.RemoveAll("TempTable");
- add an item:
repository,add(new TempTable() { Id = 1, Name = "test" });
- execute a stored procedure:
var parameters = new SqlParameter[]
                 {
                    new SqlParameter("ExecutionInstance",Guid.NewGuid()),
                    new SqlParameter("Sequence", 1)
                 };
            repository.ExecuteSQLCommand("dbo.MyProc @ExecutionInstance, @Sequence", parameters);

No comments:

Post a comment