Before implementing Repository pattern in C# ,it would be useful to first understand what is Repository Design Pattern.It is about how an application stores and retrieves data from the data stores which actually stores the application data.
In a single line we can say “Repository pattern allows development of applications which are decoupled from the underlying data storage mechanisms”. Here we will understand the need of repository pattern and how to implement repository pattern in C#
In many applications the data access logic is mixed with the business logic.This results in tight coupling of the business logic and data access logic.Any change in data access logic should not affect any other part of the application.
Application without Repository pattern
Most of the software applications follow some common architectural guidelines and consists of different layers.This is called a n-tier architecture. So an n-tier application usually consists of different tiers such as presentation tier, middle tier and the data tier.
Presentation tier provides the interface by which user interacts with the application.Middle tier includes the business functionality and also interacts with the database.Data tier defines the data storage or persistence mechanism used such as the SQL Server or the Oracle database.
Since most of the applications use a separate data storage mechanism such as SQL Server , there should be some mechanism in the application which allows the application to access the data store.This data interaction logic is commonly included in the data tier component.
Problems in Data Layer directly accessing the data store
There are few problem of including the data access functionality in the middle tier.Some of these are:
- Since the data access logic is mixed with business logic so the data access code is very likely to get duplicated in the middle tier.
- Difficulty in unit testing and automation testing of the business logic and the data access logic.
- Difficulty in implementing a common data access policy.
- If the data access code is dispersed in the business logic layer then it is very difficult to make even a small change in the data access logic.
- Code is difficult to maintain and even simple business logic change could affect other parts of the applications.Similarly change in the back end store could break the business logic.
Solution is Repository Pattern
Repository Pattern is used to create an abstract layer through which the application communicates with the data store.Examples of data stores are SQL Server ,Oracle,XML ,Lists in Sharepoint.So instead of directly accessing these data storage systems ,application communicates through this abstract Repository.
What is Repository Pattern
Repository pattern provides a solution to these problems.It acts as a mediator between the business logic layer and the data source.By implementing repository our application becomes persistent mechanism ignorant.The business logic layer directly communicates only with the repository and is not aware of the data source being used by the application.
The business logic layer makes requests for the entities to the repository,repository retrieves the entities from the database and returns them to the business layer.Business layer may also request the repository to update the entities.In this process the mapping of the database values to the entities is also taken care of by the repository so the business layer is aware of just the entities.
The repository creates an abstraction with which our business logic layer needs to deal with.What this means is that we can easily swipe specific implementation of a repository with another one easily and with no modification to our code.
The data access logic is defined in a repository rather than being mixed with the business logic layer or presentation layer so we can easily reuse the repository across the different business logic classes.
How Repository Pattern is implemented in C#
The way to implement repository pattern in our application is as:
- Define an interface for the repository.The interface declares the database operations.
- Create a class which implements the above interface and provides the implementation
for the interface methods. - Access the repository from the business layer through the interface.
It is the third step which helps create decoupled architecture and helps in unit testing.Since we are accessing the repository using the interface rather than through concrete classes we can easily point the interface to a different implementation without changing any of the business layer logic.This also helps in mocking as we can create a mock object for the repository.
In the following example we will implement repository pattern in c#.
If we have defined a Student class as:
class Student { public string Name { get; set; } public string Address { get; set; } public string RollNo { get; set; } public string Class { get; set; } }
We can define an interface for Student repository as:
interface IRepository { void Insert(Student student); void Delete(Student student); void Update(Student student); Student GetById(Student RollNo); IQueryable<Student> FetchAll(); }
The above interface defines just the signature of the methods and the different implementations of the interface can implement them differently.For example if we want to persist the Students class details in a SQL Server database then we can use the following SQLRepository class:
class SQLRepository : IRepository { public bool Insert(Student student) { //Insert logic in sql server db } public bool Delete(Student student) { //logic to delete in sql server db } public bool Update(Student student) { //logic to update in sql server db } public IQueryable<Student> GetAll() { //logic to Retreive from sql server db } public Student GetById(Student RollNo) { //logic to Retreive by id from sql server db } }
Now if a client wants to use any repository class which implements the IRepository interface then it needs to reference just the IRepository interface.
For example the below Student class defines a field of type IRepository.Since the IRepository type is being passed using constructor injection ,so the Student class is not aware of the specific class implementing the IRepository interface.
class Student { public string Name { get; set; } public string Address { get; set; } public string RollNo { get; set; } public string Class { get; set; } private IRepository _respository; public Student(IRepository repository) { _repository = respository; } public bool AddStudent(Student student) { _respository.Insert(student); } public void DeleteStudent(Student student) { _respository.Delete(student); } public void UpdateStudent(Student student) { _respository.Delete(student); } }
We can have several implementations of the same repository class.But what matters is that our business logic which is defined by the student class is oblivious to all the low level implementation details.
One of the implementations of IRepository is SQLRepository as defined above.This implementation may use ADO.NET command object.
class SQLRepository:IRepository{ private SQLComamnd _cmd; public StudentRepository(string connectionString) { _cmd=new SQLComamnd(connectionString); } public void AddStudent(Student student) { //map student properties _cmd.ExecuteNonQuery(); } public void Delete(Student student) { _cmd.ExecuteNonQuery(SQL Statement); } public void Update(Student student) { //map student properties _cmd.ExecuteNonQuery(SQL Statement); } public IQueryable<Student> GetAll() { DataTable dt=cmd.ExecuteReader(SQL Statement); //create IQueryable<Student> from datatable return IQueryableStudent; } }
Another implementation of IRepository is DBRepository .This uses Entity Framework for performing database CRUD operations.So we can easily replace any of these implementations in the Student class.
class DBRepository:IRepository { StudentDbContext studentDbContext; public StudentRepository(StudentDbContext db) { this.studentDbContext = db; } public void AddStudent(Student student) { studentDbContext.Students.Add(student); } public void Delete(Student student) { studentDbContext.Students.Remove(student); } public Student GetById(Student RollNo) { return studentDbContext.Students.Find(RollNo); } public bool Update(Student student) { studentDbContext.Entry(student).State = EntityState.Modified; } }
Generic Repository
If we use the above approach then we will be implementing a separate repository for every entity used in our application.A better approach is to use generic repository.When we create a generic repository then we define a generic class.Different entities use the same generic class.
We define a generic repository class using the following structure:
public interface IRepository<T> { List<T> GetAll(); List<T> FindById(int id); void Add(T entity); void Delete(T entity); void Edit(T entity); }
We can modify the above generic repository depending on the scenario.We can add methods as required ,in the repository.
Advantages of Repository pattern
As you can see in the above example the advantage of using the repository to access the database is that the repository class ,such as the one being referenced from the Student class above ,can be easily replaced with a a different implementation as long as it implements the IRepository interface.
So we can easily move the data persistence logic from SQL Server to Oracle without any changes to the client or the consumer class.
Since the data access logic is decoupled from the rest of the application this provides the following advantages:
- Since the data access code is located in a single place this makes it easy to change the application data access policies
- Easier to unit test.Business logic can be easily unit tested by replacing the repository with a mock.
- Persistence layer can be updated according to the current application needs without impacting the rest of the application.
Ronald Anthonisamy says
=_respository.Save(student); —> This Save() was not implemented in the IRepository class..Was this a typo or left for the reader’s understanding ?
Anyhow, thanks for explaining this repository pattern, which was a nightmare for me, all these days and now i can deal with it much more confidently.
ashish says
Thanks for the feedback.I am glad that you could understand the repository pattern.
I have corrected the typo.Thanks for pointing out.I really appreciate.Cheers!
Dennis Morgan says
I am confused. For AddStudent you have:
bool isSaved=_respository.Insert(student);
Doesn’t this mean on your interface (and concrete implementation) you should be returning a bool?
interface IRepository
{
void Insert(Student student); // should be bool Insert .. not void?
void Delete(Student student); // etc.
void Update(Student student); // etc.
Student GetById(Student RollNo);
IQueryable FetchAll();
}
Mark Neilson says
I find that a hard question to answer but ideally follow the command query separation (CQS) principle and return void. Use a separate query to get the last inserted ID value.
Joe says
Nice post. Easy to understand and illustrated well the advantages of the repository pattern
David says
This is a really clear example thanks for posting! However in all the examples I have came across online I cant seem to figure our exactly where the sql code is going I imagine it is left out as that knowledge is assumed but could I ask that you explain this in a bit more detail wish and example please.