Veloxcore blog | Always Discovering |Always Sharing

Always Discovering. Always Sharing.

26 Mar 16

Repository Pattern in C#

Repository pattern can be used in ADO.NET too with MS SQL or MySQL or whatever you want to use as data store. This abstraction helps in large application where data is coming from many data sources like database, WCF, Rest, XML or web service. Another benefit we get is testability of your data access logic.


Repository pattern in C#

The repository pattern adds a layer between the data and domain objects of an application. It also helps in testing data access layer easily without actually performing CRUD operation. Here we are saying data access layer and not EF or LINQ, that means the repository pattern can be used in ADO.NET too with MS SQL or MySQL or whatever you want to use as data store. This abstraction helps in large application where data is coming from many data sources like database, WCF, Rest, XML or web service. Another benefit we get is testability of your data access logic. You can simply create different implementation of IRepository which won’t be using actual database to perform action and hence these test cases can run without DB on any machine. The repository pattern helps us hide small nitty-gitty details of data access operation and present us with simple to use layer with testable component.

Benefit of using the repository pattern,

Clear separation/abstraction between data access way to domain objects.

Clean testable code in data access layer.

Now lets see how best we can implement this in C# using Entity Framework. There has been debate on whether you should really apply repository pattern over EF because EF already does this for you with DBSet is each repository and DBContext being your Unit Of Work. Another layer on top of it would make things redundant. But I’d justify this by saying that another abstraction over EF will give us ability to easily test our code without relying over DBContext mocking, also I’d like to treat my repositories as class which does DB operation and return me enumerables and not queryables. If you work with queryable then some fool developer comes in and take this queryable all the way to UI layer where he will create custom query and soon your project has query laid all over the place (this happened to early days of EF with Repository pattern in my project).

To implement any data access layer we need some basic methods i.e. get all records, get by ID, search, insert, update and delete.  We can keep them in base class to make keep our repository simpler and only implement methods which are complex. Lets start with creating interface for it named IRepository.

public interface IRepository
{
    IEnumerable GetAll();
    TEntity GetByID(object id);
    void Insert(TEntity entity);
    void Delete(object id);
    void Update(TEntity entityToUpdate);
}

Simple enough, now lets see the implementation of this interface. Note that we are not exposing Search method because this method will again pose the risk of having query filters written all over the place and we try to limit database related stuff to single layer (i.e. data access layer).

public abstract class Repository : IRepository where TEntity : class
{
	#region Members

	internal DbSet dbSet;
	private DataBaseContext _context;

	#endregion

	#region Constructor

	public Repository(DataBaseContext context)
	{
		dbSet = context.Set();
		_context = context;
	}

	#endregion

	#region Public Methods

	public virtual IEnumerable GetAll()
	{
		return GetRecords();
	}

	public virtual TEntity GetByID(object id)
	{
		return dbSet.Find(id);
	}

	public virtual void Insert(TEntity entity)
	{
		dbSet.Add(entity);
	}

	public virtual void Delete(object id)
	{
		TEntity entityToDelete = dbSet.Find(id);
		Delete(entityToDelete);
	}

	public virtual void Update(TEntity entityToUpdate)
	{
		dynamic entry = _context.Entry(entityToUpdate);

		if (entry.State == EntityState.Detached)
		{
			TEntity attachedEntity = dbSet.Find(GetKey(entityToUpdate));

			// You need to have access to key
			if ((attachedEntity != null))
			{
				dynamic attachedEntry = _context.Entry(attachedEntity);
				attachedEntry.CurrentValues.SetValues(entityToUpdate);
			}
			else
			{
				// This should attach entity
				entry.State = EntityState.Modified;
			}
		}
	}

	#endregion

	#region Protected Methods

	protected virtual IEnumerable GetRecords(Expression> filter = null, Func,
		IOrderedQueryable> orderBy = null, string includeProperties = "")
	{
		IQueryable query = dbSet;

		if (filter != null)
		{
			query = query.Where(filter);
		}

		foreach (string includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
		{
			query = query.Include(includeProperty);
		}

		if (orderBy != null)
		{
			return orderBy(query).ToList();
		}
		else
		{
			return query.ToList();
		}
	}

	protected virtual PagedResult GetPage(int pageIndex, int pageSize, Func,
		IOrderedQueryable> orderBy, Expression> filter = null, string includeProperties = "")
	{
		IQueryable query = dbSet;
		if (filter != null)
		{
			query = query.Where(filter);
		}

		foreach (string includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
		{
			query = query.Include(includeProperty);
		}

		return new PagedResult
		{
			Results = orderBy(query).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(),
			RowCount = query.Count()
		};
	}
	#endregion

	#region Abstract Methods
	protected abstract object[] GetKey(TEntity entity);
	#endregion
}

Notice that we have two protected methods which all inherited repositories can access. Lets now create one model, student and its search method.

public interface IStudentRepository : IRepository
{
    PagedResult GetStudentByName(string name);
}
public class StudentRepository : Repository, IStudentRepository
{
    public StudentRepository(DataBaseContext context) : base(context) { }

    protected override object[] GetKey(Student entity)
    {
        return new object[] { entity.ID };
    }

    public PagedResult GetStudentByName(string name)
    {
        return this.GetPage(1, 20, orderBy: s => s.OrderBy(o => o.FirstName).ThenBy(o => o.LastName),
            filter: s => s.FirstName == name || s.LastName == name);
    }
}

See how easily we can get filter and sorting working without actually worrying about anything else. And if you are wondering about what is PagedResult, it is a class with few properties. You can get full source code at Veloxcore Library.

Next we will talk about Unit of Work and continue expanding this library.

Like reading it, Share it.