Home > DeveloperSection > Beginner > Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC using Entity Framework

Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC using Entity Framework


ASP.Net C#  ASP.Net  ASP.NET MVC  View  Model  Controller  Entity Framework  Model Validation  Razor 
Ratings:
0 Comment(s)
 2167  View(s)
Rate this:

Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC using Entity Framework

 

 

Introduction:


The repository and unit of work patterns are intended to create an abstraction layer between the data access layer and the business logic layer of an application. Implementing these patterns can help insulate our application from changes in the data store and can facilitate automated unit testing or test-driven development (TDD).

In this article, I am implementing a repository class for Student entity type. I have created a one interface class i.e. “IStudentRepository.cs” and one repository class i.e. “”StudentRepository.cs. When we instantiate the repository in controller, we will use the interface so that the controller will accept a reference to any object that implements the repository interface. When the controller runs under a web server, it receives a repository that works with the Entity Framework. The unit of work class coordinates the work of multiple repositories by creating a single database context class shared by all of them.

  

In simple MVC application, we are not implementing this pattern because we do not know about how to create an abstraction layer between data access layer and the business logic layer of an application. Before implementing this type of repository, we have directly connected IIS server to the controller and the controller is a directly connected to a context file i.e. Database. We have not any layer between them. The Repository is providing a much facility for testing, security and divided multiple works with same context file for multiple controller, when we created a unit of work. When we use a Repository, Repository is connected with a controller and Unit of Work along with the database. And, its play a centralized role for each one, so we are easily design an application. We have also designed a generic type entity in our application that reduce the line of code and accessible for all entity types.

Note: Here, we have a one model of Student. I have also created view for curd operation separately (i.e. Create,Update,Delete,Details and Index view).  The Student model structure is as follows:

 

public class Student
        {
            [Key]
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public int StudentID { get; set; }
            [Required]
            [StringLength(50,MinimumLength=1)]
            [Display(Name = "First Name")]
            public string FirstName { get; set; }
            [Required]
            [StringLength(50, MinimumLength = 1)]
            [Display(Name = "Last Name")]
            public string LastName { get; set; }
            [DataType(DataType.DateTime)]
            [Display(Name = "Enrollment Date")]
            public DateTime EnrollmentDate { get; set; }
            [Display(Name = "Full Name")]
            public string FullName
            {
                get
                {
                    return FirstName + " " + LastName;
                }
            }
            public virtual ICollection<Enrollment> Enrollment { get; set; }
 
 
public class MyUniversityDBContext : DbContext
    {
         public DbSet<Student> Student { get; set; }
    }
   }

 

 

And here, I have created a controller of Student model, named i.e. “StudentController”.

Code is as follows:

 

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MyUniversity.Models;
using PagedList;
 
namespace MyUniversity.Controllers
{
    public class StudentController : Controller
    {
        private MyUniversityDBContext db = new MyUniversityDBContext();
 
        //
        // GET: /Student/
 
        public ViewResult Index(string sortOrder, string currentFilter, string  searchString, int? page)
        {
            ViewBag.CurrentSort = sortOrder;
            ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
            ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
 
            if (searchString != null)
            {
                page = 1;
            }
            else
            {
                searchString = currentFilter;
            }
            ViewBag.CurrentFilter = searchString;
            var students = from s in db.Student
                           select s;
            if (!String.IsNullOrEmpty(searchString))
            {
                students = students.Where(s => s.FirstName.ToUpper().Contains (searchString.ToUpper())                                   ||s.LastName.ToUpper().Contains(searchString.ToUpper()));
            }
            switch (sortOrder)
            {
                case "name_desc":
                    students = students.OrderByDescending(s => s.LastName);
                    break;
                case "Date":
                    students = students.OrderBy(s => s.EnrollmentDate);
                    break;
                case "date_desc":
                    students = students.OrderByDescending(s => s.EnrollmentDate);
                    break;
                default// Name ascending
                    students = students.OrderBy(s => s.LastName);
                    break;
            }
 
            int pageSize = 3;
            int pageNumber = (page ?? 1);
            return View(students.ToPagedList (pageNumber, pageSize));
        }
        // GET:/Student/Details/5
 
        public ActionResult Details(int id = 0)
        {
            Student student = db.Student.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }
        // GET: /Student/Create
        public ActionResult Create()
        {
            return View();
        }
        // POST: /Student/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Student student)
        {
            if (ModelState.IsValid)
            {
                db.Student.Add(student);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
 
            return View(student);
        }
 
        // GET: /Student/Edit/5
        public ActionResult Edit(int id = 0)
        {
            Student student = db.Student.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }
        // POST: /Student/Edit/5 
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(Student student)
        {
            if (ModelState.IsValid)
            {
                db.Entry(student).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(student);
        }
 
        //
        // GET: /Student/Delete/5
        public ActionResult Delete(int id = 0)
        {
            Student student = db.Student.Find(id);
            if (student == null)
            {
                return HttpNotFound();
            }
            return View(student);
        }
        // POST: /Student/Delete/5 
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Student student = db.Student.Find(id);
            db.Student.Remove(student);
            db.SaveChanges();
            return RedirectToAction("Index");
        } 
        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}

 

 

Index view:

 

@model
PagedList.IPagedList<MyUniversity.Models.Student>
@using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" />
    <script src="~/Scripts/jquery-1.8.2.min.js"></script>
<link href="~/Content/anupam.css" rel="stylesheet" />
 
@using MyUniversity.Models
  @{
    ViewBag.Title = "Index";
    var CourseList = Session["CourseList"] as List<Course>;}
 
    <h2>Index</h2>
<p>
    @Html.ActionLink("Create New""Create")
    @using (Html.BeginForm("Index", "Student", FormMethod.Get))
{
    <p>
        Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string
        <input type="submit" value="Search" />
    </p>
}<table>
   <tr>
        <th>
            @Html.ActionLink("First Name", "Index", new { sortOrder = ViewBag.NameSortParm })
        </th>
        <th>Last Name
        </th>
        <th>
            @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm })
        </th>
       <th>Full Name
        </th>
        <th></th>
  
    </tr>
 
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.FirstName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.LastName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.EnrollmentDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.FullName)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) |
            @Html.ActionLink("Details", "Details", new { id=item.StudentID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.StudentID })
        </td>
    </tr>
} 
</table>
<br/>
Page @(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 }) )
 
 

 

Create View:

 

@model
MyUniversity.Models.Student
 
@{
    ViewBag.Title = "Create";
}
 
<h2>Create</h2>
 
@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
 
    <fieldset>
        <legend>Student</legend>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.FirstName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
        </div>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.EnrollmentDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.EnrollmentDate)
            @Html.ValidationMessageFor(model =>model.EnrollmentDate)
        </div>
 
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}
 
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
 
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
 


Details view:

 

@model MyUniversity.Models.Student
 
@{
    ViewBag.Title = "Details";
}
 
<h2>Details</h2>
 
<fieldset>
    <legend>Student</legend>
 
    <div class="display-label">
         @Html.DisplayNameFor(model => model.FirstName)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.FirstName)
    </div>
 
    <div class="display-label">
         @Html.DisplayNameFor(model => model.LastName)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.LastName)
    </div>
 
    <div class="display-label">
         @Html.DisplayNameFor(model => model.EnrollmentDate)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.EnrollmentDate)
    </div>
 
    <div class="display-label">
         @Html.DisplayNameFor(model => model.FullName)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.FullName)
    </div>
</fieldset>
<p>
    @Html.ActionLink("Edit", "Edit", new { id=Model.StudentID }) |
    @Html.ActionLink("Back to List", "Index")
</p>

 

Edit View:

 

@model MyUniversity.Models.Student
 
@{
    ViewBag.Title = "Edit";
} 
<h2>Edit</h2> 
@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
 
    <fieldset>
        <legend>Student</legend>
 
        @Html.HiddenFor(model => model.StudentID)
 
        <div class="editor-label">
            @Html.LabelFor(model => model.FirstName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
        </div>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.EnrollmentDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.EnrollmentDate)
            @Html.ValidationMessageFor(model => model.EnrollmentDate)
        </div>
 
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div> 
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
 


Delete View:

 

@model MyUniversity.Models.Student
 
@{
    ViewBag.Title = "Delete";
}
 <h2>Delete</h2> 
<h3>Are you sure you want to delete this?</h3>
<fieldset>
    <legend>Student</legend> 
    <div class="display-label">
         @Html.DisplayNameFor(model => model.FirstName)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.FirstName)
    </div> 
    <div class="display-label">
         @Html.DisplayNameFor(model => model.LastName)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.LastName)
    </div> 
    <div class="display-label">
         @Html.DisplayNameFor(model => model.EnrollmentDate)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.EnrollmentDate)
    </div> 
    <div class="display-label">
         @Html.DisplayNameFor(model => model.FullName)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.FullName)
    </div>
</fieldset>
@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    <p>
        <input type="submit" value="Delete" /> |
        @Html.ActionLink("Back to List", "Index")
    </p>
}

 

 

For creating a Repository, we have follow the following step:

 

1.      Right click on project under Solution Explorer and create a new folder named i.e. “DAL 

2.      Under  DAL folder create a new  class file for interface

3.      Named i.e.  IStudentRepository

 

 And write the following code:


  public interface IStudentRepository : IDisposable
    {
        IEnumerable<Student> GetStudents();
        Student GetStudentByID(int studentId);
        void InsertStudent(Student student);
        void DeleteStudent(int studentID);
        void UpdateStudent(Student student);
        void Save();
    }

 

Now, we have created another class named “StudentRepository.cs” for implement above interface for all activities in student controller. Code is as follows:


public class StudentRepository : IStudentRepository, IDisposable
    {
        private MyUniversityDBContext context;
        public StudentRepository(MyUniversityDBContext context)
        {
            this.context = context;
        } 
        public IEnumerable<Student> GetStudents()
        {
            return context.Student.ToList();
        } 
        public Student GetStudentByID(int id)
        {
            return context.Student.Find(id);
        } 
        public void InsertStudent(Student student)
        {
            context.Student.Add(student);
        } 
        public void DeleteStudent(int studentID)
        {
            Student student = context.Student.Find(studentID);
            context.Student.Remove(student);
        }
        public void UpdateStudent(Student student)
        {
            context.Entry(student).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);
        }
    }


Output:





Now, I want to create a unit of work for the next level access. For this, I have choose a another model “Course” that are part of above”MyUniversity” project. The Course model is below:



public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int CourseID { get; set; }
        [StringLength(50, MinimumLength = 3)]
        public string Title { get; set; }
        [Range(0, 5)]
        public int Credits { get; set; }
        [Display(Name="Department")]
        public int DepartmentID { get; set; }
 
        public virtual Department Department { get; set; }
    }

 


(Note that here, Course model is same as above Student model curd operations. It means, using Course model, I have already created a view for Create,Update,Delete,Details and Index.  )

For creating a Unit of Work of Course model, we have need a generic repository because we have not want to create a separate repository for each model. So, removing this overhead we have created a GenericRepository class .(In above Course model we have need two type of repository one for Course and another for Department. So I have choose creating generic repository for both to access a single context file). Code is as follow for GenericRepository.cs:



using MyUniversity.Models;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Web;
 
namespace MyUniversity.DAL
{
    public class GenericRepository<TEntity> where TEntity : class
    {
        internal MyUniversityDBContext context;
        internal DbSet<TEntity> dbSet;
 
        public GenericRepository(MyUniversityDBContext context)
        {
            this.context = context;
            this.dbSet = context.Set<TEntity>();
        }
 
        public virtual IEnumerable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
        {
            IQueryable<TEntity> query = dbSet;
 
            if (filter != null)
            {
                query = query.Where(filter);
            }
 
            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }
 
            if (orderBy != null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }
 
        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 Delete(TEntity entityToDelete)
        {
            if (context.Entry(entityToDelete).State == EntityState.Detached)
            {
                dbSet.Attach(entityToDelete);
            }
            dbSet.Remove(entityToDelete);
        }
 
        public virtual void Update(TEntity entityToUpdate)
        {
            dbSet.Attach(entityToUpdate);
            context.Entry(entityToUpdate).State = EntityState.Modified;
        }
    }
}

 


Now, I have creating a unit of work of the Course model. Code is as follow:

 


 
using MyUniversity.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
 
namespace MyUniversity.DAL
{
    public class UnitOfWork : IDisposable
    {
        private MyUniversityDBContext context = new MyUniversityDBContext();
        private GenericRepository<Department> departmentRepository;
        private GenericRepository<Course> courseRepository; 
        public GenericRepository<Department> DepartmentRepository
        {
            get
            {
 
                if (this.departmentRepository == null)
                {
                    this.departmentRepository = new GenericRepository<Department>(context);
                }
                return departmentRepository;
            }
        }
        public GenericRepository<Course> CourseRepository
        {
            get
            {
 
                if (this.courseRepository == null)
                {
                    this.courseRepository = new GenericRepository<Course>(context);
                }
                return courseRepository;
            }
        } 
        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);
        }
    }
}


And now, change the CourseConroller is as follow:


using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MyUniversity.Models;
using MykUniversity.DAL;
namespace MyUniversity.Controllers
{
    public class CourseController : Controller
    {
        private MyUniversityDBContext db = new MyUniversityDBContext();
 
        private UnitOfWork unitOfWork = new UnitOfWork();
        //
        // GET: /Course/ 
        public ViewResult Index()
        {
            var course = unitOfWork.CourseRepository.Get(includeProperties: "Department");
            return View(course.ToList());
        } 
        //
        // GET: /Course/Details/5 
        public ActionResult Details(int id = 0)
        {
            Course course = unitOfWork.CourseRepository.GetByID(id);
            if (course == null)
            {
                return HttpNotFound();
            }
            return View(course);
        } 

        // GET: /Course/Create
        public ActionResult Create()
        {
            ViewBag.DepartmentID = new SelectList(db.Department, "DepartmentID", "Name");
            return View();
        }
        // POST: /Course/Create 
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Course course)
        {
            if (ModelState.IsValid)
            {               unitOfWork.CourseRepository.Insert(course);
                unitOfWork.Save();
                return RedirectToAction("Index");
            }
 
            ViewBag.DepartmentID = new SelectList(db.Department, "DepartmentID", "Name", course.DepartmentID);
            return View(course);
        }
        // GET: /Course/Edit/5 
        public ActionResult Edit(int id = 0)
        {
            Course course = unitOfWork.CourseRepository.GetByID(id);
            if (course == null)
            {
                return HttpNotFound();
            }
            ViewBag.DepartmentID = new SelectList(db.Department, "DepartmentID", "Name", course.DepartmentID);
            return View(course);
        }
        // POST: /Course/Edit/5 
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(Course course)
        {
            if (ModelState.IsValid)
            {                unitOfWork.CourseRepository.Update(course);
                unitOfWork.Save();
                return RedirectToAction("Index");
            }
            ViewBag.DepartmentID = new SelectList(db.Department, "DepartmentID", "Name", course.DepartmentID);
            return View(course);
        }
        // GET: /Course/Delete/5
 
        public ActionResult Delete(int id = 0)
        {
            Course course = db.Course.Find(id);
            if (course == null)
            {
                return HttpNotFound();
            }
            return View(course);
        }

        // POST: /Course/Delete/5 
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Course course = db.Course.Find(id);
            db.Course.Remove(course);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
       
        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}




Don't want to miss updates? Please click the below button!

Follow MindStick