Repository Pattern In Asp.Net Core MVC

Repository Pattern In MVC by Sagar Jaybhay

The Repository Pattern is one of the most popular patterns to create an enterprise level application. It restricts us to work directly with the data in the application and creates new layers for database operations, business logic, and the application’s UI.

Repository pattern is abstraction of the data-access layer and it hides the details about how the data is stored, retrieve from the underlying database.

In the real-life project, it is not good idea to access database logic directly in the business logic. When you write the database logic in business logic it becomes hard to test and tough to extend further.

Direct access database logic in business logic has some serious problems:

  1. Difficult for unit testing
  2. When you want to test business logic you need an external dependency of an external database if you integrate logic in the same business logic file(class).

Repository pattern separates data access logic and entity which we mapped from business logic.

When we use repository pattern the domain entities like student, employee, etc and data access logic and business logic are talk to each other by using interface means by using interface we achieve loose coupling else we have tight coupling.

Advantages of Repository pattern:

  1. Business logic tested without the need of an external database
  2. We can separately test database access logic
  3. Domain-driven development becomes easier
  4. As we centralizing data access logic so maintainability increases.

Repository pattern at least has 2 participants repository class and repository interface. In repository interface all CRUD operation signature needs to be present as we implement this interface in our repository class. Now our repository only contains information which is supported by our repository but doesn’t provide the details.

Below is our repository interface.

  public interface IStudentRepository
    {
        Student GetStudents(int StudentID);
        IEnumerable<Student> GetAllStudent();
        Student AddStudent(Student student);
        Student DeleteStudent(int studentID);
        Student UpdateStudent(Student student);
    }

Now we need to implement this interface on a repository class.

Below class is in-memory database repository class where we implement the interface.

public class StudentRepo : IStudentRepository
    {
        private List<Student> _students;

        public StudentRepo()
        {
            _students = new List<Student>() {
                new Student(){ Address="xyz",Division=Divi.A_10, FullName="Sagar",StudentId=1},
                new Student(){ Address="abc",Division=Divi.B_9, FullName="RAM",StudentId=2},
                new Student(){ Address="def",Division=Divi.A_8, FullName="LAKHAN",StudentId=3}
            };
        }

        public Student AddStudent(Student student)
        {
            var studid = _students.Max(e => e.StudentId)+1;
            student.StudentId = studid;
            _students.Add(student);
            return student;
        }

        public Student DeleteStudent(int studentID)
        {
            var stud = _students.Where(e => e.StudentId == studentID).FirstOrDefault();
            if (stud != null)
                _students.Remove(stud);
            return stud;
        }

        public IEnumerable<Student> GetAllStudent()
        {
            return this._students.AsEnumerable();
        }

        public Student GetStudents(int StudentID)
        {
            return _students.Where(r=>r.StudentId==StudentID).FirstOrDefault();
        }

        public Student UpdateStudent(Student student)
        {
            var stud = _students.Where(e => e.StudentId == student.StudentId).FirstOrDefault();
            if (stud != null)
            {
                stud.Address = student.Address;
                stud.FullName = student.FullName;
                stud.Division = student.Division;
            }

            return stud;
        }
    }

Now you can see here this interface is capable of all CRUD operation against the student type.

For SQL server database integration we create another class which in which we implement IStudentRepository interface.

Now we create OurDbContext class which implement DbContext Interface.

public class OurDbContext:DbContext
    {
        public DbSet<Student> Students { get; set; }
        public OurDbContext(DbContextOptions<OurDbContext> options):base(options)
        {
                
        }
    }

We want to inject this class object in our repository class object so we inject this by constructor injection.

public class SQLStudentRepository:IStudentRepository
    {
        private OurDbContext context;

        public SQLStudentRepository(OurDbContext context)
        {
            this.context = context;

        }
        public Student GetStudents(int StudentID)
        {
            return context.Students.Find(StudentID);
        }

        public IEnumerable<Student> GetAllStudent()
        {
            return context.Students;
        }

        public Student AddStudent(Student student)
        {
            context.Students.Add(student);
            context.SaveChanges();
            return student;
        }

        public Student DeleteStudent(int studentID)
        {
            var stud = context.Students.Find(studentID);
            if (stud != null)
            {
                context.Students.Remove(stud);
                context.SaveChanges();
            }
            return stud;
        }

        public Student UpdateStudent(Student student)
        {
            var stud = context.Students.Attach(student);
            stud.State = EntityState.Modified;
            context.SaveChanges();
            return student;
        }
    }

Now you can see we have single IStudentRepository interface which has 2 implementations. One is in-memory implementation and another is SQL Repository.

Below is home controller where we inject IStudentRepository interface into home controller. We use this injected interface in all our controller method.

Below is our home controller.

public class HomeController : Controller
    {
        private IStudentRepository _repository;

        public HomeController(IStudentRepository repository)
        {
            this._repository = repository;
        }

        //[Route("")]
        //[Route("~/")]
        //[Route("[action]")]
        public ViewResult Index()
        {
            var v = _repository.GetAllStudent();
            return View(v);
        }

       // [Route("[action]")]
        public ViewResult List()
        {
            var v = _repository.GetAllStudent();
            return View(v);
        }
      //  [Route("[action]/{id?}")]
        public ViewResult Details(int id)
        {
            HmDetailsVM hmDetailsVM = new HmDetailsVM();
            hmDetailsVM.student = _repository.GetStudents(id);
            hmDetailsVM.DivisonOfStudent = "9-A";
            ViewBag.TitleNew = "Student Info";
            return View(hmDetailsVM);
        }

        [HttpGet]
        public ViewResult Create()
        {
            return View();
        }
       
        [ActionName("CreateStudent")]
        [HttpPost]
        public IActionResult Create(Student student)
        {
            if (ModelState.IsValid)
            {
                var st = _repository.AddStudent(student);
               // return RedirectToAction("Details", new {id = st.StudentId});
            }

            return View("Create");
        }

    }

So in above controller we never mentioned anywhere which implementation we need to use so the questioned comes to your mind how controller decides which implementation we use either In-memory or SQL Repository. The answer to above questioned is we add

services.AddScoped<IStudentRepository,SQLStudentRepository>();

in our startup class ConfigureServices method.

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddTransient<IStudentRepository,StudentRepo>();
            services.AddDbContextPool<OurDbContext>(options => options.UseSqlServer(_config.GetConnectionString("StudentDBString")));
            services.AddScoped<IStudentRepository, SQLStudentRepository>();
        }

This is method in our startup class. Difference between addscoped,addtransient and addsingleton check on this link.

Repository Pattern In Asp.Net core MVC

Sagar Jaybhay, from Maharashtra, India, is currently a Senior Software Developer. He has continuously grown in the roles that he has held in the more than seven years he has been with this company. Sagar Jaybhay is an excellent team member and prides himself on his work contributions to his team and company as a whole.

Related posts