Click here to Skip to main content
15,867,453 members
Articles / Web Development / HTML

Learning MVC Part 6: Generic Repository Pattern in MVC3 Application with Entity Framework

Rate me:
Please Sign up or sign in to vote.
4.90/5 (51 votes)
17 Mar 2015CPOL8 min read 192.7K   11K   114   27
Unit of Work Pattern and Repository Pattern, and how to perform CRUD operations in an MVC application.

Introduction

Creating a Generic Repository pattern in an MVC3 application with Entity Framework is the last topic that we are about to cover in our journey of learning MVC.

The article will focus on Unit of Work Pattern and Repository Pattern, and shows how to perform CRUD operations in an MVC application when there could be a possibility of creating more than one repository class. To overcome this possibility and overhead, we make a Generic Repository class for all other repositories and implement a Unit of Work pattern to provide abstraction.

Our roadmap towards Learning MVC 

Just to remind our full roadmap towards learning MVC, 

Pre-requisites 

There are few pre-requisites before we start with the article,

  1. We have running sample application that we created in fifth part of the article series.
  2. We have Entity Framework 4.1 package or DLL on our local file system.
  3. We understand how MVC application is created (follow second part of the series).

Why Generic Repository

We have already discussed what Repository Pattern is and why do we need Repository Pattern in our last article. We created a User Repository for performing CRUD operations, but think of the scenario where we need 10 such repositories.

Are we going to create these classes? Not good, it results in a lot of redundant code. So to overcome this situation we’ll create a Generic Repository class that will be called by a property to create a new repository thus we do not result in lot of classes and also escape redundant code too. Moreover we save a lot of time that could be wasted creating those classes.

Unit of Work Pattern

According to Martin Fowler Unit of Work Pattern “Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems."

From MSDN, The Unit of Work pattern isn't necessarily something that you will explicitly build yourself, but the pattern shows up in almost every persistence tool. The ITransaction interface in NHibernate, the DataContext class in LINQ to SQL, and the ObjectContext class in the Entity Framework are all examples of a Unit of Work. For that matter, the venerable DataSet can be used as a Unit of Work.

Other times, you may want to write your own application-specific Unit of Work interface or class that wraps the inner Unit of Work from your persistence tool. You may do this for a number of reasons. You might want to add application-specific logging, tracing, or error handling to transaction management. Perhaps you want to encapsulate the specifics of your persistence tooling from the rest of the application. You might want this extra encapsulation to make it easier to swap out persistence technologies later. Or you might want to promote testability in your system. Many of the built-in Unit of Work implementations from common persistence tools are difficult to deal with in automated unit testing scenarios."

The Unit of Work class can have methods to mark entities as modified, newly created, or deleted. The Unit of Work will also have methods to commit or roll back all of the changes as well.

The important responsibilities of Unit of Work are,

  • To manage transactions.
  • To order the database inserts, deletes, and updates.
  • To prevent duplicate updates. Inside a single usage of a Unit of Work object, different parts of the code may mark the same Invoice object as changed, but the Unit of Work class will only issue a single UPDATE command to the database.

The value of using a Unit of Work pattern is to free the rest of our code from these concerns so that you can otherwise concentrate on business logic.

Why use Unit of Work?

Again Martin Fowler statements, "When you're pulling data in and out of a database, it's important to keep track of what you've changed; otherwise, that data won't be written back into the database. Similarly you have to insert new objects you create and remove any objects you delete.

You can change the database with each change to your object model, but this can lead to lots of very small database calls, which ends up being very slow. Furthermore it requires you to have a transaction open for the whole interaction, which is impractical if you have a business transaction that spans multiple requests. The situation is even worse if you need to keep track of the objects you've read so you can avoid inconsistent reads.

A Unit of Work keeps track and takes responsibility of everything you do during a business transaction that can affect the database. When you're done, it figures out everything that needs to be done to alter the database as a result of your work."

You see I don’t have to concentrate much on theory, we already have great definitions existing, all we needed is to stack them in a correct format.

Using the Unit of Work

One of the best ways to use the Unit of Work pattern is to allow disparate classes and services to take part in a single logical transaction. The key point here is that you want the disparate classes and services to remain ignorant of each other while being able to enlist in a single transaction. Traditionally, you've been able to do this by using transaction coordinators like MTS/COM+ or the newer System.Transactions namespace. Personally, I prefer using the Unit of Work pattern to allow unrelated classes and services to take part in a logical transaction because I think it makes the code more explicit, easier to understand, and simpler to unit test(From MSDN).

Creating a Generic Repository

Cut the Redundancy…

Step 1: Open up our existing MVC3 application created in Part5 in Visual Studio.

Step2: Right click Learning MVC project folder and create a folder named GenericRepository and add a class named GenericRepository.cs to that folder.

The code of the GenericRepository.cs class is as follows:

C#
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace LearningMVC.GenericRepository
{
    public class GenericRepository<TEntity> where TEntity : class
    {
        internal MVCEntities context;
        internal DbSet<TEntity> dbSet;

        public GenericRepository(MVCEntities context)
        {
            this.context = context;
            this.dbSet = context.Set<TEntity>();
        }

        public virtual IEnumerable<TEntity> Get()
        {
            IQueryable<TEntity> query = dbSet;
            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;
        }
    }
}

We can see, we have created the generic methods and the class as well is generic, when instantiating this class we can pass any model on which the class will work as a repository and serve the purpose.

TEntity is any model/domain/entity class. MVCEntities is our DBContext as discussed in earlier parts.

Step 3: Implementing UnitOfWork: Create a folder named UnitOfWork under LearningMVC project, and add a class UnitOfWork.cs to that folder.

The code of the class is as follows:

C#
using System;
using LearningMVC.GenericRepository;

namespace LearningMVC.UnitOfWork
{
    public class UnitOfWork : IDisposable
    {
        private MVCEntities context = new MVCEntities();
        private GenericRepository<User> userRepository;

        public GenericRepository<User> UserRepository
        {
            get
            {
                if (this.userRepository == null)
                    this.userRepository = new GenericRepository<User>(context);
                return userRepository;
            }
        }

        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);
        }
    }
}

We see the class implements IDisposable interface for objects of this class to be disposed.

We create object of DBContext in this class, note that earlier it was used to be passed in Repository class from a controller.

Now it's time to create our User Repository. We see in the code itself that, simply a variable named userRepository is declared as private GenericRepository<User> userRepository; of type GenericRepository serving User entity to TEntity template.

Then a property is created for the same userRepository variable in a very simplified manner,

C#
public GenericRepository<User> UserRepository
{
    get
    {
        if (this.userRepository == null)
            this.userRepository = new GenericRepository<User>(context);
        return userRepository;
    }
}

I.e., mere 6-7 lines of code. Guess what? Our UserRepository is created.

(Taken from Google)

You see it was as simple as that, you can create as many repositories you want by just creating simple properties, and no need to create separate classes. And now you can complete the rest of the story by yourself, confused???? Yes it's DBOperations, let's do it.

Step 4: In MyController, declare a variable unitOfWork as:

C#
private UnitOfWork.UnitOfWork unitOfWork = new UnitOfWork.UnitOfWork();

Now this unitOfWork instance of UnitOfWork class holds all th repository properties,if we press “." After it, it will show the repositories.So we can choose any of the repositories created and perform CRUD operations on them.

E.g. our Index action:

C#
public ActionResult Index()
{
    var userList = from user in unitOfWork.UserRepository.Get() select user;
    var users = new List<LearningMVC.Models.UserList>();
    if (userList.Any())
    {
        foreach (var user in userList)
        {
            users.Add(new LearningMVC.Models.UserList() { UserId = user.UserId, 
              Address = user.Address, Company = user.Company, 
              FirstName = user.FirstName, LastName = user.LastName, 
              Designation = user.Designation, EMail = user.EMail, PhoneNo = user.PhoneNo });
        }
    }
    ViewBag.FirstName = "My First Name";
    ViewData["FirstName"] = "My First Name";
    if(TempData.Any())
    {
        var tempData = TempData["TempData Name"];
    }
    return View(users);
}

Here,

  • unitOfWork.UserRepository ­­> Accessing UserRepository.
  • unitOfWork.UserRepository.Get() -> Accessing Generic Get() method to get all users.

Earlier we used to have MyController constructor like:

C#
public MyController()
{
    this.userRepository = new UserRepository(new MVCEntities());
}

Now, no need to write that constructor, in fact you can remove the UserRepository class and Interface we created in part 5 of Learning MVC.

I hope you can write the Actions for rest of the CRUD operations as well.

Details

C#
public ActionResult Details(int id)
{
    var userDetails = unitOfWork.UserRepository.GetByID(id);
    var user = new LearningMVC.Models.UserList();
    if (userDetails != null)
    {
        user.UserId = userDetails.UserId;
        user.FirstName = userDetails.FirstName;
        user.LastName = userDetails.LastName;
        user.Address = userDetails.Address;
        user.PhoneNo = userDetails.PhoneNo;
        user.EMail = userDetails.EMail;
            user.Company = userDetails.Company;
        user.Designation = userDetails.Designation;
    }
    return View(user);
}

Create:

C#
[HttpPost]
public ActionResult Create(LearningMVC.Models.UserList userDetails)
{
    try
    {
        var user = new User();
        if (userDetails != null)
        {
            user.UserId = userDetails.UserId;
            user.FirstName = userDetails.FirstName;
            user.LastName = userDetails.LastName;
            user.Address = userDetails.Address;
            user.PhoneNo = userDetails.PhoneNo;
            user.EMail = userDetails.EMail;
            user.Company = userDetails.Company;
            user.Designation = userDetails.Designation;
        }
        unitOfWork.UserRepository.Insert(user);
        unitOfWork.Save();
        return RedirectToAction("Index");
          }
    catch
    {
        return View();
    }
}

Edit:

C#
public ActionResult Edit(int id)
{
    var userDetails = unitOfWork.UserRepository.GetByID(id);
    var user = new LearningMVC.Models.UserList();
    if (userDetails != null)
    {
        user.UserId = userDetails.UserId;
        user.FirstName = userDetails.FirstName;
        user.LastName = userDetails.LastName;
        user.Address = userDetails.Address;
        user.PhoneNo = userDetails.PhoneNo;
        user.EMail = userDetails.EMail;
        user.Company = userDetails.Company;
        user.Designation = userDetails.Designation;
      }
    return View(user);
}

[HttpPost]
public ActionResult Edit(int id, User userDetails)
{
    TempData["TempData Name"] = "Akhil";

    try
    {
        var user = unitOfWork.UserRepository.GetByID(id);
        user.FirstName = userDetails.FirstName;
        user.LastName = userDetails.LastName;
        user.Address = userDetails.Address;
        user.PhoneNo = userDetails.PhoneNo;
             user.EMail = userDetails.EMail;
        user.Company = userDetails.Company;
        user.Designation = userDetails.Designation;
        unitOfWork.UserRepository.Update(user);
        unitOfWork.Save();
        return RedirectToAction("Index");
    }

Delete:

C#
public ActionResult Delete(int id)
{
    var user = new LearningMVC.Models.UserList();
    var userDetails = unitOfWork.UserRepository.GetByID(id);

    if (userDetails != null)
    {
        user.FirstName = userDetails.FirstName;
        user.LastName = userDetails.LastName;
        user.Address = userDetails.Address;
        user.PhoneNo = userDetails.PhoneNo;
        user.EMail = userDetails.EMail;
        user.Company = userDetails.Company;
        user.Designation = userDetails.Designation;
    }
    return View(user);
}

[HttpPost]
public ActionResult Delete(int id, LearningMVC.Models.UserList userDetails)
{
    try
    {
        var user = unitOfWork.UserRepository.GetByID(id);

        if (user != null)
        {
            unitOfWork.UserRepository.Delete(id);
            unitOfWork.Save();
        }

        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

Note: Images are taken from Google images.

Conclusion

We now know how to make generic repositories too, and perform CRUD operations using it.

We have also learnt UnitOfWork pattern in detail. Now you are qualified and confident enough to apply these concepts in your enterprise applications. This was the last part of this MVC series, let me know if you feel to discuss any topic in particular or we can also start any other series as well.

Happy coding :) 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect https://codeteddy.com/
India India
Akhil Mittal is two times Microsoft MVP (Most Valuable Professional) firstly awarded in 2016 and continued in 2017 in Visual Studio and Technologies category, C# Corner MVP since 2013, Code Project MVP since 2014, a blogger, author and likes to write/read technical articles, blogs, and books. Akhil is a technical architect and loves to work on complex business problems and cutting-edge technologies. He has an experience of around 15 years in developing, designing, and architecting enterprises level applications primarily in Microsoft Technologies. He has diverse experience in working on cutting-edge technologies that include Microsoft Stack, AI, Machine Learning, and Cloud computing. Akhil is an MCP (Microsoft Certified Professional) in Web Applications and Dot Net Framework.
Visit Akhil Mittal’s personal blog CodeTeddy (CodeTeddy ) for some good and informative articles. Following are some tech certifications that Akhil cleared,
• AZ-304: Microsoft Azure Architect Design.
• AZ-303: Microsoft Azure Architect Technologies.
• AZ-900: Microsoft Azure Fundamentals.
• Microsoft MCTS (70-528) Certified Programmer.
• Microsoft MCTS (70-536) Certified Programmer.
• Microsoft MCTS (70-515) Certified Programmer.

LinkedIn: https://www.linkedin.com/in/akhilmittal/
This is a Collaborative Group

779 members

Comments and Discussions

 
QuestionOne request for few in depth articles on unit testing Pin
Tridip Bhattacharjee4-May-17 22:52
professionalTridip Bhattacharjee4-May-17 22:52 
QuestionSmall problem - pattern breaks down for Many to Many relationships Pin
Brian C Hart7-Jun-15 18:33
professionalBrian C Hart7-Jun-15 18:33 
QuestionUOW Pin
Pascualito29-May-15 5:07
professionalPascualito29-May-15 5:07 
GeneralMy vote of 5 Pin
Rajat_Chanana28-Apr-15 23:38
Rajat_Chanana28-Apr-15 23:38 
GeneralRe: My vote of 5 Pin
Akhil Mittal29-Apr-15 0:13
professionalAkhil Mittal29-Apr-15 0:13 
GeneralRe: My vote of 5 Pin
Akhil Mittal18-May-15 18:52
professionalAkhil Mittal18-May-15 18:52 
QuestionThanks for the series, My vote 5 Pin
Brittle16189-Apr-15 19:28
Brittle16189-Apr-15 19:28 
AnswerRe: Thanks for the series, My vote 5 Pin
Akhil Mittal4-May-15 19:56
professionalAkhil Mittal4-May-15 19:56 
GeneralMy vote of 5 Pin
Erion Pici7-Apr-15 17:39
Erion Pici7-Apr-15 17:39 
GeneralRe: My vote of 5 Pin
Akhil Mittal4-May-15 19:56
professionalAkhil Mittal4-May-15 19:56 
Thanks for reading. i am glad u liked it Smile | :)

EF approach is different from LINQ to SQl one.We should not compare them.
EF is for enterprise level applications and it is an ORM, in a way that it could attach to different database servers as well, but in case of LINQ to SQL , you'll have to stick with SQL only, and in future if you want to use MySql or any other db in your application, then again you'll have to switch the approach , most probably to EF Smile | :) Thumbs Up | :thumbsup: Thumbs Up | :thumbsup:
Thanks
Do not forget to comment and rate the article if it helped you by any means.

QuestionCan I use structuremap IoC with this? Pin
tunminhein24-Aug-14 6:23
tunminhein24-Aug-14 6:23 
QuestionException handling and Unit Of Work Pin
IanHo27-Aug-13 18:57
IanHo27-Aug-13 18:57 
AnswerRe: Exception handling and Unit Of Work Pin
Akhil Mittal28-Aug-13 0:00
professionalAkhil Mittal28-Aug-13 0:00 
GeneralMy vote of 4 Pin
manish1498226-Aug-13 3:33
manish1498226-Aug-13 3:33 
GeneralRe: My vote of 4 Pin
Akhil Mittal26-Aug-13 22:03
professionalAkhil Mittal26-Aug-13 22:03 
GeneralMy vote of 5 Pin
LoveJenny22-Aug-13 16:54
LoveJenny22-Aug-13 16:54 
GeneralRe: My vote of 5 Pin
Akhil Mittal26-Aug-13 22:03
professionalAkhil Mittal26-Aug-13 22:03 
GeneralMy vote of 5 Pin
meetp21-Aug-13 18:54
meetp21-Aug-13 18:54 
GeneralMy vote of 5 Pin
Chris Dis21-Aug-13 6:11
professionalChris Dis21-Aug-13 6:11 
GeneralMy vote of 5 Pin
swas.agr21-Aug-13 6:03
swas.agr21-Aug-13 6:03 
QuestionMapping properties from POCO to ViewModel Pin
Member 463587421-Aug-13 1:11
Member 463587421-Aug-13 1:11 
AnswerRe: Mapping properties from POCO to ViewModel Pin
Akhil Mittal21-Aug-13 5:54
professionalAkhil Mittal21-Aug-13 5:54 
Questionmaster details crud using this pattern Pin
Md. YaSiR aRaFaT20-Aug-13 23:13
Md. YaSiR aRaFaT20-Aug-13 23:13 
GeneralMy vote of 5 Pin
Md. YaSiR aRaFaT20-Aug-13 22:58
Md. YaSiR aRaFaT20-Aug-13 22:58 
GeneralRe: My vote of 5 Pin
Akhil Mittal21-Aug-13 5:57
professionalAkhil Mittal21-Aug-13 5:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.