This article explains the Repository Pattern in MVC applications using the Entity Framework.
First we need to understand what the Repository Pattern is. See the following Image.
MVC Application without Repository Pattern
MVC Application with Repository pattern
The Repository Pattern separates the data access logic and maps it to the entities in the business logic.
Now we will see the Repository Pattern with a sample application.
Open Visual Studio 2012 and go to "File" -> "New" -> "Project...".
Now Add a ADO.NET Entity Data Model to your application. Right-click on the project in the Solution Explorer then select Add -> ADO.NET Entity Data Model.
Now add a new folder to your application named DAL.
Right-click on the DAL Folder then select Add Interface -> IEmployeeRepository.cs.
Now my IEmployeeRepository.cs is as in the following:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using RepositoryPatternInMVCWithEntityFramework.Models;
- using System.Data;
-
- namespace RepositoryPatternInMVCWithEntityFramework.DAL
- {
- public class EmployeeRepository : IEmployeeRepository, IDisposable
- {
- private EmployeeManagementEntities context;
-
- public EmployeeRepository(EmployeeManagementEntities context)
- {
- this.context = context;
- }
-
- public IEnumerable<Employee> GetAllEmployee()
- {
- return context.Employee.ToList();
- }
-
- public Employee GetEmployeeByID(int id)
- {
- return context.Employee.Find(id);
- }
-
- public void InsertEmployee(Employee emp)
- {
- context.Employee.Add(emp);
- }
-
- public void DeleteEmployee(int emp_ID)
- {
- Employee emp = context.Employee.Find(emp_ID);
- context.Employee.Remove(emp);
- }
-
- public void UpdateEmployee(Employee emp)
- {
- context.Entry(emp).State = EntityState.Modified;
- }
-
- public void Save()
- {
- context.SaveChanges();
- }
-
- private bool disposed = false;
-
- protected virtual void Dispose(bool disposing)
- {
- if (!this.disposed)
- {
- if (disposing)
- {
- context.Dispose();
- }
- }
- this.disposed = true;
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- }
- }
Now right-click on the Controller folder in your project in the Solution Explorer then select Add New Empty Controller -> EmployeeController and provide the following code.
EmployeeController - using RepositoryPatternInMVCWithEntityFramework.DAL;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Mvc;
- using RepositoryPatternInMVCWithEntityFramework.Models;
- using PagedList;
- using System.Data;
-
- namespace RepositoryPatternInMVCWithEntityFramework.Controllers
- {
- public class EmployeeController : Controller
- {
- private IEmployeeRepository employeeRepository;
-
- public EmployeeController()
- {
- this.employeeRepository = new EmployeeRepository(new EmployeeManagementEntities());
- }
-
- public EmployeeController(IEmployeeRepository employeeRepository)
- {
- this.employeeRepository = employeeRepository;
- }
-
- public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)
- {
- ViewBag.CurrentSort = sortOrder;
- ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Emp_ID" : "";
- if (searchString != null)
- {
- page = 1;
- }
- else
- {
- searchString = currentFilter;
- }
- ViewBag.CurrentFilter = searchString;
-
- var employees = from s in employeeRepository.GetAllEmployee()
- select s;
- if (!String.IsNullOrEmpty(searchString))
- {
- employees = employees.Where(s => s.Name.ToUpper().Contains(searchString.ToUpper())
- || s.Name.ToUpper().Contains(searchString.ToUpper()));
- }
- switch (sortOrder)
- {
- case "Emp ID":
- employees = employees.OrderByDescending(s => s.Emp_ID);
- break;
- case "Name":
- employees = employees.OrderBy(s => s.Name);
- break;
- case "State":
- employees = employees.OrderByDescending(s => s.State);
- break;
- case "Country":
- employees = employees.OrderByDescending(s => s.Country);
- break;
- default:
- employees = employees.OrderBy(s => s.Emp_ID);
- break;
- }
-
- int pageSize = 5;
- int pageNumber = (page ?? 1);
- return View(employees.ToPagedList(pageNumber, pageSize));
- }
-
-
-
- public ViewResult Details(int id)
- {
- Employee emp = employeeRepository.GetEmployeeByID(id);
- return View(emp);
- }
-
-
-
- public ActionResult Create()
- {
- return View();
- }
-
-
-
- [HttpPost]
- [ValidateAntiForgeryToken]
- public ActionResult Create(
- [Bind(Include = "Name, Email, Designation,City, State, Country")]
- Employee emp)
- {
- try
- {
- if (ModelState.IsValid)
- {
- employeeRepository.InsertEmployee(emp);
- employeeRepository.Save();
- return RedirectToAction("Index");
- }
- }
- catch (Exception ex)
- {
- ModelState.AddModelError(string.Empty, "Some Error Occured.");
- }
- return View(emp);
- }
-
-
-
- public ActionResult Edit(int id)
- {
- Employee emp = employeeRepository.GetEmployeeByID(id);
- return View(emp);
- }
-
-
-
- [HttpPost]
- [ValidateAntiForgeryToken]
- public ActionResult Edit(Employee emp)
- {
- try
- {
- if (ModelState.IsValid)
- {
- employeeRepository.UpdateEmployee(emp);
- employeeRepository.Save();
- return RedirectToAction("Index");
- }
- }
- catch (Exception ex)
- {
- ModelState.AddModelError(string.Empty, "Some error Occured.");
- }
- return View(emp);
- }
-
-
-
- public ActionResult Delete(bool? saveChangesError = false, int id = 0)
- {
- if (saveChangesError.GetValueOrDefault())
- {
- ViewBag.ErrorMessage = "Some Error Occured.";
- }
- Employee emp = employeeRepository.GetEmployeeByID(id);
- return View(emp);
- }
-
-
-
- [HttpPost]
- [ValidateAntiForgeryToken]
- public ActionResult Delete(int id)
- {
- try
- {
- Employee emp = employeeRepository.GetEmployeeByID(id);
- employeeRepository.DeleteEmployee(id);
- employeeRepository.Save();
- }
- catch (Exception ex)
- {
- return RedirectToAction("Delete", new { id = id, saveChangesError = true });
- }
- return RedirectToAction("Index");
- }
-
- protected override void Dispose(bool disposing)
- {
- employeeRepository.Dispose();
- base.Dispose(disposing);
- }
-
- }
- }
Now add a View. Right-click on the Index Action Method and select Add view.
Index.cshtml
- @using PagedList.Mvc;
-
- @model PagedList.IPagedList<RepositoryPatternInMVCWithEntityFramework.Employee>
- <link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
-
- @{
- ViewBag.Title = "Employee Management System";
- }
-
- <h2>Employee Management System</h2>
-
-
- @using (Html.BeginForm("Index", "Employee", FormMethod.Get))
- {
- <p style="background-color:red; color:white; font-size:16pt; padding:10px;">
- Search Employee By Name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
- <input type="submit" value="Search" />
- @Html.ActionLink("Add New Employee", "Create")
- </p>
- }
- <table style="background-color:white;">
- <tr>
- <th></th>
- <th style="width: 100px;">
- @Html.ActionLink("Emp ID", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter = ViewBag.CurrentFilter })
- </th>
- <th>
- @Html.ActionLink("Name", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter = ViewBag.CurrentFilter })
- </th>
- <th>Email
- </th>
- <th>Designation
- </th>
- <th>City
- </th>
- <th>
- @Html.ActionLink("State", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter = ViewBag.CurrentFilter })
- </th>
- <th>
- @Html.ActionLink("Country", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter = ViewBag.CurrentFilter })
- </th>
- <th style="width: 150px;"></th>
- </tr>
-
- @foreach (var item in Model)
- {
- <tr>
- <td></td>
- <td>
- @Html.DisplayFor(modelItem => item.Emp_ID)
- </td>
- <td style="width:130px;">
- @Html.DisplayFor(modelItem => item.Name)
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.Email)
- </td>
- <td style="width:140px;">
- @Html.DisplayFor(modelItem => item.Designation)
- </td>
- <td style="width:120px;">
- @Html.DisplayFor(modelItem => item.City)
- </td>
- <td style="width:120px;">
- @Html.DisplayFor(modelItem => item.State)
- </td>
- <td>
- @Html.DisplayFor(modelItem => item.Country)
- </td>
- <td style="width:270px;">
- @Html.ActionLink("Edit", "Edit", new { id = item.Emp_ID }) |
- @Html.ActionLink("Details", "Details", new { id = item.Emp_ID }) |
- @Html.ActionLink("Delete", "Delete", new { id = item.Emp_ID })
- </td>
- </tr>
- }
-
- </table>
- <br />
- <div style="background-color:orange; padding-left:15px; padding-top:10px;">
- Showing Records @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount
- @Html.PagedListPager(Model, page => Url.Action("Index", new { page, sortOrder = ViewBag.CurrentSort,currentFilter = ViewBag.CurrentFilter }))
- </div>
Here I am using PagedList. You can add the reference of PagedList by right-clicking on the project in the Solution Explorer and selecting Manage NuGet Packages.
Now add a Detail View by right-clicking on the Details Action method. (Make it Strongly Typed View with class Employee and Scaffold template Details.)
Details.cshtml
- @model RepositoryPatternInMVCWithEntityFramework.Employee
-
- <h2>Employee Details</h2>
-
- <table>
- <tr>
- <td>@Html.DisplayNameFor(model => model.Emp_ID)</td>
- <td>@Html.DisplayFor(model => model.Emp_ID)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.Name)</td>
- <td>@Html.DisplayFor(model => model.Name)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.Email)</td>
- <td>@Html.DisplayFor(model => model.Email)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.Designation)</td>
- <td>@Html.DisplayFor(model => model.Designation)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.City)</td>
- <td>@Html.DisplayFor(model => model.City)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.State)</td>
- <td>@Html.DisplayFor(model => model.State)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.Country)</td>
- <td>@Html.DisplayFor(model => model.Country)</td>
- </tr>
- <tr style="background-color: orange; padding: 25px;">
- <td></td>
- <td>@Html.ActionLink("Edit", "Edit", new { id = Model.Emp_ID }) |
- @Html.ActionLink("Back to List", "Index")</td>
-
- </tr>
- </table>
Now create a View by right-clicking on the Create Action Method.
Create.cshtml
- @model RepositoryPatternInMVCWithEntityFramework.Employee
-
- <script src="~/Scripts/jquery-1.7.1.min.js"></script>
- <script src="~/Scripts/jquery.validate.min.js"></script>
- <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
-
- @using (Html.BeginForm())
- {
- @Html.AntiForgeryToken()
- @Html.ValidationSummary(true)
-
- <fieldset>
- <legend>Employee</legend>
-
- <div class="editor-label">
- @Html.LabelFor(model => model.Name)
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.Name)
- @Html.ValidationMessageFor(model => model.Name)
- </div>
-
- <div class="editor-label">
- @Html.LabelFor(model => model.Email)
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.Email)
- @Html.ValidationMessageFor(model => model.Email)
- </div>
-
- <div class="editor-label">
- @Html.LabelFor(model => model.Designation)
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.Designation)
- @Html.ValidationMessageFor(model => model.Designation)
- </div>
-
- <div class="editor-label">
- @Html.LabelFor(model => model.City)
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.City)
- @Html.ValidationMessageFor(model => model.City)
- </div>
-
- <div class="editor-label">
- @Html.LabelFor(model => model.State)
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.State)
- @Html.ValidationMessageFor(model => model.State)
- </div>
-
- <div class="editor-label">
- @Html.LabelFor(model => model.Country)
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.Country)
- @Html.ValidationMessageFor(model => model.Country)
- </div>
-
- <p>
- <input type="submit" value="Create" />
- </p>
- </fieldset>
- }
-
- <div>
- @Html.ActionLink("Back to List", "Index")
- </div>
Now add an Edit View by right-clicking on the Edit Action Method.
Edit.cshtml
- @model RepositoryPatternInMVCWithEntityFramework.Employee
-
- <script src="~/Scripts/jquery-1.7.1.min.js"></script>
- <script src="~/Scripts/jquery.validate.min.js"></script>
- <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
- <h2>Edit Employee information</h2>
-
- @using (Html.BeginForm())
- {
- @Html.AntiForgeryToken()
-
- @Html.ValidationSummary(true)
-
- <table>
- <tr>
- <td>@Html.LabelFor(model => model.Emp_ID)</td>
- <td>
- @Html.EditorFor(model => model.Emp_ID, new { disabled = "disabled", @readonly = "readonly" })
-
- @Html.ValidationMessageFor(model => model.Emp_ID)</td>
- </tr>
-
- <tr>
- <td>@Html.LabelFor(model => model.Name)
- </td>
- <td>@Html.EditorFor(model => model.Name)
- @Html.ValidationMessageFor(model => model.Name)</td>
- </tr>
-
- <tr>
- <td>@Html.LabelFor(model => model.Email)</td>
- <td>
- @Html.EditorFor(model => model.Email)
- @Html.ValidationMessageFor(model => model.Email)</td>
- </tr>
- <tr>
- <td>@Html.LabelFor(model => model.Designation)</td>
- <td>
- @Html.EditorFor(model => model.Designation)
- @Html.ValidationMessageFor(model => model.Designation)
- </td>
- </tr>
- <tr>
- <td>@Html.LabelFor(model => model.City)</td>
- <td>
- @Html.EditorFor(model => model.City)
- @Html.ValidationMessageFor(model => model.City)</td>
- </tr>
- <tr>
- <td>@Html.LabelFor(model => model.State)</td>
- <td>@Html.EditorFor(model => model.State)
- @Html.ValidationMessageFor(model => model.State)</td>
- </tr>
- <tr>
- <td>@Html.LabelFor(model => model.Country)</td>
- <td>
- @Html.EditorFor(model => model.Country)
- @Html.ValidationMessageFor(model => model.Country)</td>
- </tr>
- <tr style="background-color: orange; padding: 25px;">
- <td></td>
- <td>
- <input type="submit" value="Save" />
- @Html.ActionLink("Back to List", "Index")
- </td>
- </tr>
- </table>
- }
Now add a Delete View by right-clicking on the Delete Action Method.
Delete.cshtml
- @model RepositoryPatternInMVCWithEntityFramework.Employee
-
- <h3>Are you sure you want to delete this?</h3>
- <table>
- <tr>
- <td>@Html.DisplayNameFor(model => model.Emp_ID)</td>
- <td>@Html.DisplayFor(model => model.Emp_ID)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.Name)</td>
- <td>@Html.DisplayFor(model => model.Name)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.Email)</td>
- <td>@Html.DisplayFor(model => model.Email)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.Designation)</td>
- <td>@Html.DisplayFor(model => model.Designation)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.City)</td>
- <td>@Html.DisplayFor(model => model.City)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.State)</td>
- <td>@Html.DisplayFor(model => model.State)</td>
- </tr>
- <tr>
- <td>@Html.DisplayNameFor(model => model.Country)</td>
- <td>@Html.DisplayFor(model => model.Country)</td>
- </tr>
- </table>
-
- @using (Html.BeginForm())
- {
- @Html.AntiForgeryToken()
-
- <table>
- <tr style="background-color: orange; padding: 25px;">
- <td></td>
- <td>
- <input type="submit" value="Delete" />
-
- @Html.ActionLink("Back to List", "Index")
- </td>
-
- </tr>
- </table>
- }
Now run the application.
Showing All Records with Paging and Sorting.
Now go to page 3 .
Now do a search.
Now click on Add New Employer.
Now click on any of the records Edit button.
Now click on Details.
Now click on the Delete option.
To perform CRUD operations you will see that we are not calling the DB method directly here. We are using the Repository Pattern.
For this application my Employee Table in design mode is:
The following is the script of my table:
- CREATE TABLE [dbo].[Employee](
- [Emp_ID] [int] IDENTITY(1,1) NOT NULL,
- [Name] [varchar](50) NULL,
- [Email] [varchar](500) NULL,
- [Designation] [varchar](50) NULL,
- [City] [varchar](50) NULL,
- [State] [varchar](50) NULL,
- [Country] [varchar](50) NULL,
- CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED
- (
- [Emp_ID] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
- ON [PRIMARY]) ON [PRIMARY]
-
- GO
-
- SET ANSI_PADDING OFF
- GO
The following is the data in my table:
Enjoy programming.
Next I will write about the Generic Repository Pattern.