Search code examples
c#asp.net-mvcentity-frameworkrepository-patterndata-access-layer

Repository Pattern using Entity Framework, code first and CRUD operations


I'm trying to get a practical understanding on how to properly implement the repository pattern. Ive created a model class in my MVC web application called Employees, iv'e created a context class and a connectionstring to generate a databas. Ive also created a Controller with read/wright actions using entity frame work wich brings some CRUD operations like Update, Delete etc.

If i have understand the repository pattern correctly i should place all data accessing logic in the repository.I also believe i need to create an IRepository interface for the context class to inherrit from.

In my controller i have these basic CRUD operation. In my case, should all the logic within these action methods be moved to my repository class?

Controller:

public class EmployeeController : Controller
{
    private _dbCrudApplicationContext db = new _dbCrudApplicationContext();

    // GET: Employee
    public ActionResult Index()
    {
        return View(db.Employees.ToList());
    }

    // GET: Employee/Details/5
    public ActionResult Details(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Employee employee = db.Employees.Find(id);
        if (employee == null)
        {
            return HttpNotFound();
        }
        return View(employee);
    }

    // GET: Employee/Create
    public ActionResult Create()
    {
        return View();
    }

    // POST: Employee/Create
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "Id,FirstName,LastName,EmployeeNumber,Department,HasValidEmployeeCertificate")] Employee employee)
    {
        if (ModelState.IsValid)
        {
            db.Employees.Add(employee);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(employee);
    }

    // GET: Employee/Edit/5
    public ActionResult Edit(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Employee employee = db.Employees.Find(id);
        if (employee == null)
        {
            return HttpNotFound();
        }
        return View(employee);
    }

    // POST: Employee/Edit/5
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "Id,FirstName,LastName,EmployeeNumber,Department,HasValidEmployeeCertificate")] Employee employee)
    {
        if (ModelState.IsValid)
        {
            db.Entry(employee).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(employee);
    }

    // GET: Employee/Delete/5
    public ActionResult Delete(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Employee employee = db.Employees.Find(id);
        if (employee == null)
        {
            return HttpNotFound();
        }
        return View(employee);
    }

    // POST: Employee/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public ActionResult DeleteConfirmed(int id)
    {
        Employee employee = db.Employees.Find(id);
        db.Employees.Remove(employee);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

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

Ive started adding the "Index" method to the repository but i'm uncertain how to implement the other methods as they are more complexed. Should i move all the logic within the action methods in the Controller or just part of the code?

Repository:

public class CustomerRepository : _dbCrudApplicationContext
{
    _dbCrudApplicationContext db = new _dbCrudApplicationContext();


    public List<Employee> GetAllEmployees()
    {
        return db.Employees.ToList();    
    }
}

Solution

  • A repository should not try to extend a model context, instead it should use this context to perform certain actions. A Repository is defined as

    A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes

    The main reason behind the interface style is to allow unit testing to take place.

    In your case, the repository might look like

    public interface ICustomerRepository
    {
       public List<Employee> GetAllEmployees();
    }
    
    public class CustomerRepository : ICustomerRepository
    {
       private _dbCrudApplicationContext db;
    
       public CustomerRepository(_dbCrudApplicationContext context)
       {
         db = context;
       }
    
       public List<Employee> GetAllEmployees()
       {
          return db.Employees.ToList();
       }
    }
    

    The repository when completed should contain all operations to be carried out on the model. These will need to be migrated from the MVC views.

    See http://blog.gauffin.org/2013/01/repository-pattern-done-right/ for more details.