Basic Generic Repository Pattern and Unity of Work Frame Work in ASP.NET MVC3 : Part 1

Description

First of all I will discuss the general repository pattern in this article; later we will discuss the generic repository pattern.

The repository and unit of work patterns are used to create an abstraction layer between the data access layer and the business logic layer of an application.
It is very useful on automated unit testing or test-driven development (TDD).

Basically it is a design pattern implemented through Interface features.

Normally when using the Entity Framework we are calling the Entity Framework class object in the controller class for accessing the entity classes. It is tightly coupled.

So for a loosely coupled mechanism we will implement the repository pattern where we will write our business logic with the Entity Framework classes.

And for the controller class we have to write the less code for our business operations.

GntcResPtrn1.gif

(Image taken from Google)

In this example we will create first 2 model classes under the "Models" Folder.

  1. Student has a one to many relation with the Enrolment class
  2. class
  3. Enrolment

We will create a "Dal" Folder in our Project.

We will create a "Repository" folder in our project. See the following image; you will get the idea:

GntcResPtrn2.gif

Add this piece of code in "_Layout.cshtml" under the "Shared" Folder.

<nav>
                <ul id="menu">
                   <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Students", "Index", "Student")</li>

                </ul>

Step 1:

Now first create our 2 model classes.

public class Student
    {
        public int StudentID { get; set; }

        [Required(ErrorMessage = "Last name is required.")]
        [Display(Name="Last Name")]
        [MaxLength(50)]
        public string LastName { get; set; }

        [Required(ErrorMessage = "First name is required.")]
        [Column("FirstName")]
        [Display(Name = "First Name")]
        [MaxLength(50)]
        public string FirstMidName { get; set; }

        [Required(ErrorMessage = "Enrollment date is required.")]
        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
        [Display(Name = "Enrollment Date")]
        public DateTime? EnrollmentDate { get; set; }

        public string FullName
        {
            get
            {
                return LastName + ", " + FirstMidName;
            }
        }

        public virtual ICollection<Enlorment> Enlorment { get; set; }
    }

And the second class is:

public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public decimal? Grade { get; set; }
        public virtual Student Student { get; set; }
    }


Step 2:

After that we will create a Poco Class for the Entity Framework with using the 2 classes.

Under the "Dal" folder create a new class named "SchoolContext.cs" and copy the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using UniversityProject.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace UniversityProject.Dal
{
    public class SchoolContext:DbContext
    {
        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }

}
}

Now our poco class for the Entity Class is ready. This is our DAL layer.

Change the web config file by adding this code.

  <connectionStrings>
    <
add name="SchoolContext" connectionString="data source=.;Database=University;Trusted_Connection=true;" providerName="System.Data.SqlClient"
/>   
  </connectionStrings>

Step 3:

In the Reposioiry folder, create a class file named IStudentRepository.cs .

GntcResPtrn3.gif

The code is like:

public interface IStudentRepository
    {
        IEnumerable<Student> GetStudents();
        Student GetStudentbyId(int id);
        void InsertStudent(Student student);
        void UpdateStudent(Student student);
        void DeleteStudent(int studentID);
        void Save();

    }

This code declares a typical set of CRUD methods, including two read methods.

One that returns all "Student" entities, and one that finds a single Student entity by ID.

Step 4:

In the DAL folder, create a class file named "StudentRepository.cs" Class file.

GntcResPtrn4.gif

Here is the code

  public class StudentRepository:IStudentRepository
    {
        public SchoolContext SchoolContext;
        public StudentRepository(SchoolContext SchoolContext)
        {
            this.SchoolContext = SchoolContext;
        }
        public IEnumerable<Student> GetStudents()
        {
            return SchoolContext.Students.ToList();
        }
        public Student GetStudentbyId(int id)
        {
            return SchoolContext.Students.Find(id);
        }
        public void InsertStudent(Student student)
        {
             SchoolContext .Students .Add (student );
        }

        public void DeleteStudent(int id)
        {
            Student stud = SchoolContext.Students.Find(id);
            SchoolContext.Students.Remove(stud);

        }
        public void UpdateStudent(Student Student)
        {
            SchoolContext.Entry(Student).State = EntityState.Modified;
        }
        public void Save()
        {
            SchoolContext.SaveChanges();
        }

    }

Step 5:

Now We have to create our "studentController.cs" class under our "Controller" folder.

Please see the following step:

GntcResPtrn5.gif

Choose appropriate template fields like shown in the Image above (marked with red).

After clicking the "ADD" button the "studentController.cs" class will be created and the corresponding "Razor view" page will also be generated with insert, update , delete, details and Index Pages.

It will look like the following picture:

GntcResPtrn6.gif

Step 6:

In StudentController.cs, replace the code currently in the class with the following code:

public class StudentController : AsyncController
    {
        public IStudentRepository studentRepository;
        StudentRepository StudentRepository = new StudentRepository(new SchoolContext());
        private SchoolContext db = new SchoolContext();
        public StudentController()
        {
            this.studentRepository = new StudentRepository(new SchoolContext());
        }
        public StudentController(IStudentRepository StudentRepository)
        {
            this.studentRepository = StudentRepository;
        }

        //private SchoolContext db = new SchoolContext();

        //
        // GET: /Student/
        public ViewResult Index(string currentFilter, int? page)
        {
            if (Request.HttpMethod != "POST")
            {
                page = 1;
            }
            var students = from s in studentRepository.GetStudents()
                           select s;

            int pageSize = 5;
            int pageNumber = (page ?? 1); 

            // return View(students.ToList());
            return View(students.ToPagedList(pageNumber, pageSize));
        }
       [HttpPost ]
        public ActionResult  Index(string sortOrder, string searchString, string currentFilter, int? page)
        {
            if (this.Request.IsAjaxRequest())
            {
                string filepath = this.Request.AppRelativeCurrentExecutionFilePath.ToString();
                searchString = filepath.Split('/').LastOrDefault();
                ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name desc" : "Name Asc";
                ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date";
                if (Request.HttpMethod == "POST")
                {
                    if (currentFilter != null)
                        searchString = currentFilter;
                }
                else
                {
                    //searchString = currentFilter;
                    page = 1;
                }
                ViewBag.CurrentFilter = searchString;
            }

            var students = from s in db.Students
                           select s;

            switch (sortOrder)
            {
                case "Name desc":             
                    students = students.OrderByDescending(s=>s.LastName );
                    break;
                case "Name Asc":
                    students = students.OrderByDescending(s => s.FirstMidName);
                    break;
                case "Date":
                    students = students.OrderBy(s => s.EnrollmentDate);
                    break;
                case "Date desc":
                    students = students.OrderByDescending(s => s.EnrollmentDate);
                    break;
                default:
                    students = students.OrderByDescending(s => s.FirstMidName);
                    break;
            }
            if (!String.IsNullOrEmpty(searchString))
            {
                students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())
                                       || s.FirstMidName.ToUpper().Contains(searchString.ToUpper()));
            }
            int pageSize = 1;
            int pageNumber = (page ?? 1);

         return View(students.ToPagedList(pageNumber, pageSize));
           
//return PartialView("_List", students.ToPagedList(pageNumber, pageSize));

        }
        /// <summary>
        /// return value calling from autocomplete textbox
        /// </summary>
        /// <param name="term"></param>
        ///
<returns></returns>

        public JsonResult AutocompleteSuggestions(string term)
        {
            var suggestions = from s in db.Students
                              select s.FirstMidName ;
            var namelist = suggestions.Where(n => n.ToLower().StartsWith(term.ToLower()));
           // return namelist.ToList();
            return Json(namelist, JsonRequestBehavior.AllowGet);
        }

        //
       
// GET: /Student/Details/5

        public ViewResult Details(int id)
        {
           
//Student student = db.Students.Find(id);

            Student student = StudentRepository.GetStudentbyId(id);
          return View(student);

        }

        //
       
// GET: /Student/Create

        public ActionResult Create()
        {
            return View();
        }

        //
        // POST: /Student/Create
 
        [HttpPost]
        public ActionResult Create(Student student)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    StudentRepository.InsertStudent(student);
                    StudentRepository.Save();
                    return RedirectToAction("Index");
                }
            }
            catch(DataException)
            {
                ModelState.AddModelError("","Eror while saving");
            }

            return View(student);
        }

        //
       
// GET: /Student/Edit/5

        public ActionResult Edit(int id)
        {
            Student student = StudentRepository.GetStudentbyId(id);
            return View(student);
        }

        //
       
// POST: /Student/Edit/5

        [HttpPost]
        public ActionResult Edit(Student student)
        {
            if (ModelState.IsValid)
            {
                StudentRepository.UpdateStudent(student);
                StudentRepository.Save();
                return RedirectToAction("Index");
            }
            return View(student);
        }

        //
       
// GET: /Student/Delete/5

        public ActionResult Delete(int id, bool? saveChangesError)
        {
            if (saveChangesError.GetValueOrDefault())
            {
                ViewBag.ErrorMessage = "Unable to save changes. Try again, and if the problem persists see your system administrator.";
            }
            Student student = StudentRepository.GetStudentbyId(id);
            return View(student);
        }

        //
       
// POST: /Student/Delete/5

        [HttpPost, ActionName("Delete")]
        public ActionResult DeleteConfirmed(int id)
        {
            try
            {
                //Student student = db.Students.Find(id);
                //db.Students.Remove(student);
                //db.SaveChanges();
                ////Student studentToDelete = new Student() { StudentID = id };
                ////db.Entry(studentToDelete).State = EntityState.Deleted;
                //Student student = new Student() { StudentID = id };
                //db.Entry(student).State = EntityState.Deleted;
                //db.SaveChanges();
                Student Stud = StudentRepository.GetStudentbyId(id);
                StudentRepository.DeleteStudent(id);
                StudentRepository.Save(); 

            }
            catch (DataException)
            {
                //Log the error (add a variable name after DataException)
                return RedirectToAction("Delete",
                    new System.Web.Routing.RouteValueDictionary
                { "id", id }, 
                { "saveChangesError", true } });
            }
            return RedirectToAction("Index");

        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
}

The controller now declares a class variable for an object that implements the IStudentRepository interface instead of the context class:

private IStudentRepository studentRepository;

The default constructor creates a new context instance, and an optional constructor allows the caller to pass in a context instance.

public StudentController()
{
this.studentRepository = new StudentRepository(new SchoolContext());
}

public StudentController(IStudentRepository studentRepository)
{
this.studentRepository = studentRepository;
}

In the CRUD methods, the repository is now called instead of the context (normally we called in controller class):

var students = from s in studentRepository.GetStudents()
select s;
Student student = studentRepository.GetStudentByID(id);
studentRepository.InsertStudent(student);
studentRepository.Save();
studentRepository.UpdateStudent(student);
studentRepository.Save();
studentRepository.DeleteStudent(id);
studentRepository.Save();
And the Dispose method now disposes the repository instead of the context:

studentRepository.Dispose();

Now run the application.

In my next article I will show you what is unity of framework and how it is used in Generic Repository pattern.

Conclusion

So in this article we have learned what a repository pattern is in ASP.Net MVC3 and how to use it.