본문 바로가기
Software design pattern

ASP.NET Core - MVC] Repository Pattern

by Fastlane 2023. 4. 25.
728x90
반응형

Repository Pattern이란?

데이터, 도메인, Data Access Layers(Entity Framework Core 또는 Dapper)사이의 중재 역할을 한다. 

데이터를 저장 또는 가져오는데 필요한 로직을 갖는 클래스이다. 

 

Repository Pattern의 장점

중복 쿼리 감소

Repository에 data access code를 작성하고, 여러개의 Controllers/Libraries에서 호출해서 사용할 수 있다. 

 

Data Access Layer로부터 Application의 De-couples

ASP.NET Core에는 다양한 ORM이 유효하다. 현재 가장 유명한 것은 Entity Framework Core이다. 하지만 시간이 지나면서 새로운 기술이 발전함에 따라 새로운 ORM을 사용해야 할 수도 있다.

또는 여러개의 ORM을 하나의 프로젝트에 사용할 수도 있다. 앱의 퍼포먼스 최적화를 위해 Dapper는 data를 가져오기 위해 사용하고, EFCore는 data를 저장하기 위해 사용할 수 있다. 

Repository Pattern은 DataAccess Layer를 추상화한다. 앱은 더이상 어떠한 ORM에도 종속되지 않으며 EFCore는 그 중 하나의 옵션이다. 

 

Generic Repository

CRUD 동작은 entity만 다를뿐이지 계속 반복되는 동작이다. 따라서 Generic을 사용하여 어떠한 Entity Repository에서도 사용할 수 있는 Base Repository를 만들자. 

IRepoBase.cs

using System.Linq.Expressions;

namespace SampleProject.Data
{
    public interface IRepoBase<T>
    {
        IQueryable<T> FindAll();
        IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression);
        T GetById(int id);
        void Create(T entity);
        void Update(T entity);
        void Delete(T entity);
    }
}

RepoBase.cs

using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;

namespace SampleProject.Data
{
    public abstract class RepoBase<T> : IRepoBase<T> where T : class
    {
        protected AppDbContext _context { get; set; }
        public RepoBase(AppDbContext context)
        {
            _context = context;
        }
        public IQueryable<T> FindAll() => _context.Set<T>().AsNoTracking();
        public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression) =>
            _context.Set<T>().Where(expression).AsNoTracking();
        public T GetById(int id) => _context.Set<T>().Find(id);
        public void Create(T entity) => _context.Set<T>().Add(entity);
        public void Update(T entity) => _context.Set<T>().Update(entity);
        public void Delete(T entity) => _context.Set<T>().Remove(entity);
    }
}

Generic Repository 상속과 확장

이제 Entity Repository에서 RepoBase<T> abstract class를 상속받아서 사용해보자. 

IUserRepo.cs

using SampleProject.Models;

namespace SampleProject.Data
{
    public interface IUserRepo : IRepoBase<User>
    {
        bool SaveChanges();
        IEnumerable<User> GetAllUsers();
        User GetUserById(string id);
    }
}

UserRepo.cs

using SampleProject.Models;

namespace SampleProject.Data
{
    public class UserRepo : RepoBase<User>, IUserRepo
    {
        private readonly AppDbContext _context;

        public UserRepo(AppDbContext context) : base(context)
        {
            _context = context;
        }

        public IEnumerable<User> GetAllUsers()
        {
            return FindAll().ToList();
        }

        public User GetUserById(string id)
        {
            return FindByCondition(owner => owner.EmpId.Equals(id)).FirstOrDefault();
        }

        public bool SaveChanges()
        {
            return (_context.SaveChanges() >= 0);
        }
    }
}

Program.cs

builder.Services.AddScoped<IUserRepo, UserRepo>();

Controller에서 사용

using Microsoft.AspNetCore.Mvc;
using SampleProject.Data;
using SampleProject.Models;
using System.Diagnostics;

namespace SampleProject.Controllers
{
    public class HomeController : Controller
    {
        private readonly IUserRepo _repository;
        private readonly ILogger<HomeController> _logger;

        public HomeController(IUserRepo repository, ILogger<HomeController> logger)
        {
            _repository = repository;
            _logger = logger;
        }
        
        public IActionResult Index()
        {
            var users = _repository.GetAllUsers();
            return View(users);
        }
    }
}

 

728x90
반응형

댓글