In this article, I am going to explain the generic repository pattern. We are going to start the concept and implementation of the repository pattern, so first, let's try to understand what it is.
Why go with the repository pattern?
Enterprise applications such as web-based enterprise applications, order management systems, payroll, accounting applications, etc., are large-scale data flow applications. Along with data flow, they have some other features too.
- Large amount of data: These types (accounting, payroll, order management) of applications handle quite a large amount of data. And this amount of data is not fixed. It may vary day by day and get more and more than the previous day.
- Separation of database and application: Handling of data for an application is a major undertaking that needs some persistent storage. We usually separate the database engine from the application. The data object is a multiuser environment. At a given point in time, the data should be overwritten and given a concurrency feature.
- Modification in business rules is always acceptable: Enterprise applications will always have new rules, so our application should be ready to accept those changes without affecting the past code and logic. These rules can change at any time. At the time of business upgrade, our application should be ready to accept all the changes. Sometimes, business rules have operations that usually deal with the size (data) and display the data on many pages or one page, we may have to display the data from different tables.
We come across these scenarios daily and reach one conclusion, that is,
Enterprise applications function in three different areas
- Data Access: This layer deals with the data storage and retrieval, such as CRUD (Create, Read, Update, Delete)
- Business Rule: This layer deals with data access such as reading and writing data and encapsulation with business-specific rules
- User Interface: Displays the data to the end user and also accepts input from the end user.
We use a repository pattern when the scenario is dealing with a complex set of queries from multiple tables, to avoid duplication so that we will not write the same code at multiple places.
In these scenarios, we use a layer between our domain class and the data access layer. This layer will take care of all operations that can be reused again and again. In short, we can say that "Repository pattern plays a mediator's role in between the data-access layer and all the system."
Once the repository pattern is implemented, the client code won’t invoke the data access directly. Instead, we will invoke the repository to get the job done. The repository offers a collection interface by providing methods to add, modify, remove, and fetch domain objects.
Let’s try to implement a generic repository pattern in the ASP MVC application.
Prerequisites
- asp.net MVC 4
- SQL server
- entity framework (code first approach)
Before going to the implementation part, it is a very common question usually asked by many developers - Entity Framework is already developed in a repository pattern. Then, why will we again create a repository pattern?
I am trying to answer that.
In a small application, it is not required at all, but in enterprise applications, it is beneficial.
- Promoting loose coupling: In the repository pattern, all query code is isolated from the Entity Framework objects, so that it gives a good way to implement loose coupling with other model classes.
- Preventing Entity Framework objects from business logic layers: Giving all data access (Entity Framework objects) to the business layer is not a good idea. All LINQ queries get embedded inside it, these things are not desirable.
Steps to implement generic repository in ASP.NET MVC
Step 1. Add a new MVC template
Choose an MVC template.
Step 2. Add Entity Framework
Step 3. We are going to choose code first approach for creating a database and respective tables.
After defining the model class for students, now, we are now going to make a schema class for the code-first approach.
[Table("Students")]
public class Student
{
[Key]
[Display(Name = "Student Id")]
public int StId { get; set; }
[Required]
[Display(Name = "Student Name")]
public string StName { get; set; }
[Display(Name = "Address")]
public string StAddress { get; set; }
[Required]
[Display(Name = "Mobile No.")]
public string MobileNo { get; set; }
}
Step 4. Create studentConext
Step 5. Add database set Initializer
Usually, in Entity Framework, there are four different ways to create an Initializer.
- CreateDatabaseIfNotExists: This is the default initializer and it will create the database if it does not exist already, as per the configuration. One more thing; it will throw an exception if the model class is changed and will try to run the application with this initializer.
- DropCreateDatabaseIfModelChanges: According to this initializer, it drops an existing database and creates a new database if model classes (entity classes) are changed.
- DropCreateDatabaseAlways: According to this initializer, it drops an existing database every time when we run the application. It doesn’t worry about the model class changes. This is useful when we want a fresh database every time we run the application.
- Customer db Initializer: We can create our own custom initializer.
Syntax
public class SchoolDataBaseInitializer : CreateDatabaseIfNotExists<StudentContext>
{
protected override void Seed(StudentContext context)
{
base.Seed(context);
}
}
For this application, I am implementing the “DropCreateDatabaseIfModelChanges” database initializer method, globally.aspx file.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Database.SetInitializer<StudentContext>(new DropCreateDatabaseIfModelChanges<StudentContext>());
}
}
Step 6. Now working with Generic Repository
Add an Interface.
public interface IGenericRepo<T> where T : class
{
IEnumerable<T> GetAll();
T GetById(int id);
void Insert(T obj);
void Update(T obj);
void Delete(int id);
void Save();
}
As we can see Interface is very simple and easy to understand here, Here <T> I took to incorporate with any classes whoever needed.
Inside Interface, we are implementing two type parameters - T1, (type parameter is a concept which is taken in C++ for making the generic concept). Here, I am not saying both are the same. The T1 type parameter specifies the class type, which means that we pass the class while implementing the generic interface to any class, and on the other hand, I haven’t specified the T2 with any data, the reason is that - it might be any class having unique id in different type (might be int, string or all).
Implement the generic repository with Entity Framework.
public class GenericRepo<T> : IGenericRepo<T> where T : class
{
private StudentContext _context = null;
private DbSet<T> table = null;
public GenericRepo()
{
this._context = new StudentContext();
table = _context.Set<T>();
}
public GenericRepo(StudentContext _context)
{
this._context = _context;
table = _context.Set<T>();
}
public IEnumerable<T> GetAll()
{
return table.ToList();
}
public T GetById(int id)
{
return table.Find(id);
}
public void Insert(T obj)
{
table.Add(obj);
}
public void Update(T obj)
{
table.Attach(obj);
_context.Entry(obj).State = EntityState.Modified;
}
public void Delete(int id)
{
T existing = table.Find(id);
table.Remove(existing);
}
public void Save()
{
_context.SaveChanges();
}
}
Now, it is very easy to implement the controller class to play with CRUD operation with the Repository class. As the code stated first we are creating a private member of our context class, and assigning it to null. same as with the Dbset.
Two constructors I created, as default and parameterized for initializing the context. As the context is get initilize we will able to call implemented Generic methods.
Have a look at the fetch and insert records using the Controller class.
public class HomeController : Controller
{
private IGenericRepo<Students> repository = null;
public HomeController()
{
this.repository = new GenericRepo<Students>();
}
public HomeController(IGenericRepo<Students> repository)
{
this.repository = repository;
}
public ActionResult Index()
{
var obj = repository.GetAll();
return View(obj);
}
[HttpGet]
public ActionResult AddStudent()
{
return View();
}
[HttpPost]
public ActionResult AddStudent(Students obj)
{
repository.Insert(obj);
return RedirectToAction("Index");
}
}
Output
Summary
This article explained the generic repository pattern, this approach where we can handle multiple databases with a single implemented class, only we have to pass the respective class to operate the desired result. We have also seen the pros and cons of the generic repository pattern.