내용으로 건너뛰기
ASP.NET MVC App에서 리포지토리 패턴 구현하기

ASP.NET MVC App에서 리포지토리 패턴 구현하기

리포지토리 패턴은 엔터프라이즈 수준 응용 프로그램을 만드는 데 가장 많이 사용되는 패턴 중 하나입니다. 이는 애플리케이션의 데이터로 직접 작업하도록 제한하고 데이터베이스 작업, 비즈니스 로직 및 애플리케이션의 UI를 위한 새로운 계층을 생성합니다.

21min read

리포지토리 패턴은 엔터프라이즈 수준 응용 프로그램을 만드는 데 가장 많이 사용되는 패턴 중 하나입니다. 이는 애플리케이션의 데이터로 직접 작업하도록 제한하고 데이터베이스 작업, 비즈니스 로직 및 애플리케이션의 UI를 위한 새로운 계층을 생성합니다. 응용 프로그램이 리포지토리 패턴을 따르지 않으면 다음과 같은 문제가 발생할 수 있습니다.

  • 데이터베이스 작업 코드 중복
  • 데이터베이스 작업 및 비즈니스 로직을 단위 테스트하기 위한 UI 필요
  • 단위 테스트 비즈니스 로직에 대한 외부 종속성의 필요성
  • 데이터베이스 캐싱 등을 구현하기 어렵습니다.

저장소 패턴을 사용하면 다음과 같은 많은 이점이 있습니다.

  • 비즈니스 로직은 데이터 액세스 로직 없이 단위 테스트를 수행할 수 있습니다.
  • 데이터베이스 액세스 코드는 재사용할 수 있습니다.
  • 데이터베이스 액세스 코드는 중앙에서 관리되므로 캐싱과 같은 모든 데이터베이스 액세스 정책을 쉽게 구현할 수 있습니다.
  • domain logic를 구현하는 것은 쉽습니다.
  • 도메인 엔터티 또는 비즈니스 엔터티는 주석으로 강력하게 입력됩니다. 그리고 더.

인터넷에는 리포지토리 패턴에 대해 작성된 수백만 개의 기사가 있지만 이 기사에서는 ASP.NET MVC 애플리케이션에서 구현하는 방법에 중점을 둘 것입니다. 자, 시작하겠습니다!

계속해서 ASP.NET MVC의 저장소 패턴에 대해 배우기 전에 웹 응용 프로그램을 더 빠르게 작성하고 실행하는 데 도움이 되는 jQuery 기반 라이브러리 Ignite UI Infragistics 확인하는 것이 좋습니다. Ignite UI for JavaScript 라이브러리를 사용하여 HTML5, jQuery, Angular, React 또는 ASP.NET MVC에서 복잡한 LOB 요구 사항을 빠르게 해결할 수 있습니다. (여기에서 Ignite UI의 무료 평가판을 다운로드할 수 있습니다.)

프로젝트 구조

응용 프로그램에 대한 프로젝트 구조를 만드는 것부터 시작하겠습니다. 우리는 네 가지 프로젝트를 만들 것입니다.

  1. 핵심 프로젝트
  2. 인프라 프로젝트
  3. 테스트 프로젝트
  4. MVC Project

각 프로젝트에는 고유한 목적이 있습니다. 프로젝트의 이름을 보면 코어 프로젝트와 인프라 프로젝트는 클래스 라이브러리, 웹 프로젝트는 MVC 프로젝트, 테스트 프로젝트는 유닛 테스트 프로젝트입니다. 결국 솔루션 탐색기의 프로젝트는 아래 이미지와 같이 표시됩니다.

 Web project is an MVC

이 게시물을 진행하면서 각 프로젝트의 목적에 대해 자세히 배우게 되지만 시작하기 위해 각 프로젝트의 주요 목표를 다음과 같이 요약할 수 있습니다.

다양한 프로젝트에 대한 이해가 명확합니다.

지금까지 다양한 프로젝트에 대한 우리의 이해는 명확합니다. 이제 각 프로젝트를 하나씩 구현해 보겠습니다. 구현하는 동안 각 프로젝트의 책임을 자세히 살펴 보겠습니다.

핵심 프로젝트

핵심 프로젝트에서는 엔터티와 리포지토리 인터페이스 또는 데이터베이스 작업 인터페이스를 유지합니다. 핵심 프로젝트에는 도메인 엔터티 및 도메인 엔터티에 필요한 데이터베이스 작업에 대한 정보가 포함되어 있습니다. 이상적인 시나리오에서는 핵심 프로젝트가 외부 라이브러리에 대한 종속성을 갖지 않아야 합니다. 비즈니스 로직, 데이터베이스 작업 코드 등이 없어야 합니다.

요컨대, 핵심 프로젝트에는 다음이 포함되어야 합니다.

  • 도메인 엔터티
  • 도메인 엔터티에 대한 리포지토리 인터페이스 또는 데이터베이스 작업 인터페이스Repository interfaces or database operations interfaces on domain entities
  • 도메인별 데이터 주석

핵심 프로젝트에는 다음이 포함될 수 없습니다.

  • 데이터베이스 작업을 위한 모든 외부 라이브러리
  • Business logic
  • 데이터베이스 작업 코드

도메인 엔터티를 만드는 동안 다음과 같이 도메인 엔터티 속성에 대한 제한 사항에 대한 결정도 내려야 합니다.

  • 특정 속성이 필요한지 여부. 예를 들어 Product 엔터티의 경우 제품 이름은 필수 속성이어야 합니다.
  • 특정 속성의 값이 지정된 범위 내에 있는지 여부. 예를 들어, Product 엔터티의 경우 price 속성은 지정된 범위에 있어야 합니다.
  • 특정 속성의 최대 길이에 값을 지정하지 않아야 하는지 여부. 예를 들어 Product 엔터티의 경우 name 속성 값은 최대 길이보다 작아야 합니다.

도메인 엔터티 속성에 이러한 데이터 주석이 많이 있을 수 있습니다. 이러한 데이터 주석에 대해 생각할 수 있는 두 가지 방법이 있습니다.

  1. 도메인 엔터티의 일부로
  2. 데이터베이스 작업 논리의 일부로

데이터 주석을 보는 방법은 순전히 우리에게 달려 있습니다. 데이터베이스 작업의 일부로 간주되면 데이터베이스 작업 라이브러리 API를 사용하여 제한을 적용할 수 있습니다. 인프라 프로젝트의 데이터베이스 작업에 Entity Framework를 사용할 것이므로 Entity Framework Fluent API를 사용하여 데이터에 주석을 달 수 있습니다.

도메인의 일부로 간주하면 System.ComponentModel.DataAnnotations 라이브러리를 사용하여 데이터에 주석을 달 수 있습니다. 이를 사용하려면 Core 프로젝트의 Reference 폴더를 마우스 오른쪽 버튼으로 클릭하고 Add Reference를 클릭합니다. 프레임워크 탭에서 System.ComponentModel.DataAnnotations를 선택하고 프로젝트에 추가합니다.

ProductApp을 만들고 있으므로 Product 엔터티를 만드는 것부터 시작하겠습니다. 엔터티 클래스를 추가하려면 Core 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 클래스를 추가한 다음 클래스 이름을 Product로 지정합니다.

using System.ComponentModel.DataAnnotations;
namespace ProductApp.Core
{
    public class Product
    {
        public int Id { get; set; }
 [Required]
 [MaxLength(100)]
        public string Name { get; set; }
 [Required]
        public double Price { get; set; }
        public bool inStock { get; set; }
    }
}

Product 엔터티 속성에 RequiredMaxLength로 주석을 달았습니다. 이 두 주석은 모두 System.ComponentModel.DataAnnotations의 일부입니다. 여기에서는 제한을 도메인의 일부로 간주하여 핵심 프로젝트 자체에서 데이터 주석을 사용했습니다.

Product Entity 클래스를 만들고 여기에 데이터 주석도 적용했습니다. 이제 저장소 인터페이스를 만들어 보겠습니다. 그러나 우리가 그것을 만들기 전에 저장소 인터페이스가 무엇인지 이해합시다.

리포지토리 인터페이스는 도메인 엔터티에서 가능한 모든 데이터베이스 작업을 정의합니다. 도메인 엔터티에서 수행할 수 있는 모든 데이터베이스 작업은 도메인 정보의 일부이므로 리포지토리 인터페이스를 핵심 프로젝트에 넣을 것입니다. 이러한 작업을 수행하는 방법은 인프라 프로젝트의 일부입니다.

리포지토리 인터페이스를 만들려면 Core 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 Interfaces라는 폴더를 추가합니다. Interfaces 폴더가 생성되면 Interface 폴더를 마우스 오른쪽 버튼으로 클릭하고 새 항목 추가를 선택한 다음 Code 탭에서 Interface를 선택합니다. 인터페이스 이름을 IProductRepository로 지정합니다.

using System.Collections.Generic;

namespace ProductApp.Core.Interfaces
{
    public interface IProductRepository
    {
        void Add(Product p);
        void Edit(Product p);
        void Remove(int Id);
        IEnumerable GetProducts(); Product FindById(int Id); 
    } 
} 

이제 Product 엔티티 클래스와 Product Repository Interface를 만들었습니다. 이 시점에서 핵심 프로젝트는 다음과 같아야 합니다.

 solution tree

계속해서 핵심 프로젝트를 구축하여 모든 것이 제자리에 있는지 확인하고 인프라 프로젝트를 만들기 위해 진행해 보겠습니다.

인프라 프로젝트

인프라 프로젝트의 주요 목적은 데이터베이스 작업을 수행하는 것입니다. 데이터베이스 작업 외에도 웹 서비스를 사용하고 IO 작업을 수행하는 등의 작업을 수행할 수 있습니다. 따라서 주로 인프라 프로젝트는 다음 작업을 수행할 수 있습니다.

  • Database operations
  • WCF 및 웹 서비스 작업
  • IO operations

모든 데이터베이스 기술을 사용하여 데이터베이스 작업을 수행할 수 있습니다. 이 게시물에서는 Entity Framework를 사용할 것입니다. 따라서 Code First 접근 방식을 사용하여 데이터베이스를 만들 것입니다. Code First 접근 방식에서 데이터베이스는 클래스를 기반으로 만들어집니다. 여기서 데이터베이스는 Core Project의 Domain 엔터티를 기반으로 생성됩니다.

핵심 프로젝트 도메인 엔터티에서 데이터베이스를 만들려면 다음 작업을 수행해야 합니다.

  1. Create DataContext class
  2. 연결 문자열 구성
  3. 데이터베이스에 데이터를 시드하기 위해 DataBase 이니셜라이저 클래스 만들기
  4. IProductRepsitory 인터페이스 구현

 

참조 추가

먼저 Entity Framework 및 ProductApp.Core 프로젝트에 대한 참조를 추가해 보겠습니다. Entity Framework를 추가하려면 인프라 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 Nuget 패키지 관리를 클릭합니다. Package Manager Window에서 Entity Framework를 검색하고 안정적인 최신 버전을 설치합니다.

ProductApp.Core 프로젝트의 참조를 추가하려면 인프라 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 참조 추가를 클릭합니다. 참조 창에서 프로젝트 탭을 클릭하고 ProductApp.Core를 선택합니다.

DataContext class

DataContext 클래스의 목표는 Entity Framework Code First 접근 방식에서 DataBase를 만드는 것입니다. DataContext 클래스의 생성자에 연결 문자열을 전달합니다. 연결 문자열을 읽으면 Entity Framework에서 데이터베이스를 만듭니다. 연결 문자열을 지정하지 않으면 Entity Framework는 로컬 데이터베이스 서버에 데이터베이스를 만듭니다.

In the DataContext class:

  • DbSet 형식 속성을 만듭니다. 이는 Product 엔터티에 대한 테이블을 만드는 작업을 담당합니다
  • DataContext 클래스의 생성자에서 연결 문자열을 전달하여 데이터베이스를 만들기 위한 정보(예: 서버 이름, 데이터베이스 이름, 로그인 정보 등)를 지정합니다. 연결 문자열의 이름을 전달해야 합니다. 데이터베이스가 생성될 이름입니다.
  • 연결 문자열이 전달되지 않으면 Entity Framework는 로컬 데이터베이스 서버에 데이터 컨텍스트 클래스의 이름으로 만듭니다.
  • productdatacontext 클래스는 DbContext 클래스를 상속합니다

ProductDataContext 클래스는 아래 목록과 같이 만들 수 있습니다.

using ProductApp.Core;
using System.Data.Entity;

namespace ProductApp.Infrastructure
{
    public class ProductContext  : DbContext
    {
        public ProductContext()  : base("name=ProductAppConnectionString") {}
        public DbSet Products { get; set; }
    }
} 

다음으로 연결 문자열에 대해 작업해야 합니다. 앞에서 설명한 것처럼 연결 문자열을 전달하여 데이터베이스 생성 정보를 지정하거나 Entity Framework에 응답하여 기본 위치에 기본 데이터베이스를 만들 수 있습니다. ProductDataContext 클래스의 생성자에 연결 문자열 이름 ProductAppConnectionString을 전달한 이유인 연결 문자열을 지정할 것입니다. App.Config 파일에서 아래 목록과 같이 ProductAppConnectionString 연결 문자열을 만들 수 있습니다.

 <add name="ProductAppConnectionString" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ProductAppJan;Integrated Security=True;MultipleActiveResultSets=true" providerName="System.Data.SqlClient"/>

데이터베이스 이니셜라이저 클래스

데이터베이스 이니셜라이저 클래스를 만들어 생성 시 일부 초기 값으로 데이터베이스를 시드합니다. Database 이니셜라이저 클래스를 만들려면 DropCreateDatabaseIfModelChnages에서 상속되는 클래스를 만듭니다. 데이터베이스 이니셜라이저 클래스를 만들기 위해 상속할 수 있는 다른 클래스 옵션이 있습니다. DropCreateDatabaseIfModelChnages 클래스를 상속하면 모델에 새 데이터베이스가 생성될 때마다 모델이 변경됩니다. 예를 들어 Product 엔터티 클래스에서 속성을 추가하거나 제거하면 Entity Framework는 기존 데이터베이스를 삭제하고 새 데이터베이스를 만듭니다. 물론 데이터도 손실되기 때문에 이것은 좋은 옵션은 아니므로 데이터베이스 이니셜라이저 클래스를 상속하기 위해 다른 옵션을 탐색하는 것이 좋습니다.

데이터베이스 이니셜라이저 클래스는 아래 목록과 같이 만들 수 있습니다. 여기서는 두 개의 행으로 제품 테이블을 시드합니다. 데이터를 시드하려면 다음을 수행합니다.

  1. Override Seed method
  2. Context.Products에 제품 추가
  3. Context.SaveChanges() 호출
using ProductApp.Core;
using System.Data.Entity;

namespace ProductApp.Infrastructure {
  public class ProductInitalizeDB : DropCreateDatabaseIfModelChanges {
    protected override void Seed(ProductContext context) {
      context.Products.Add(
          new Product { Id = 1, Name = "Rice", inStock = true, Price = 30 });
      context.Products.Add(
          new Product { Id = 2, Name = "Sugar", inStock = false, Price = 40 });
      context.SaveChanges();
      base.Seed(context);
    }
  }
}

지금까지 데이터베이스를 만들기 위해 모든 Entity Framework Code First 관련 작업을 수행했습니다. 이제 구체적인 ProductRepository 클래스에서 Core 프로젝트의 IProductRepository 인터페이스를 구현해 보겠습니다.

Repository Class

Product Entity에서 데이터베이스 작업을 수행하는 클래스입니다. 이 클래스에서는 Core 프로젝트의 IProductRepository 인터페이스를 구현합니다. 인프라 프로젝트에 ProductRepository 클래스를 추가하고 IProductRepository 인터페이스를 구현하는 것부터 시작하겠습니다. 데이터베이스 작업을 수행하기 위해 간단한 LINQ to Entity 쿼리를 작성할 것입니다. 제품 저장소 클래스는 아래 목록과 같이 생성할 수 있습니다.

using ProductApp.Core.Interfaces;
using System.Collections.Generic;
using System.Linq;
using ProductApp.Core;

namespace ProductApp.Infrastructure {
  public class ProductRepository : IProductRepository {
    ProductContext context = new ProductContext();
    public void Add(Product p) {
      context.Products.Add(p);
      context.SaveChanges();
    }

    public void Edit(Product p) {
      context.Entry(p).State = System.Data.Entity.EntityState.Modified;
    }

    public Product FindById(int Id) {
            var result = (from r in context.Products where r.Id == Id select r).FirstOrDefault();
            return result;
    }

    public IEnumerable GetProducts() {
      return context.Products;
    }
    public void Remove(int Id) {
      Product p = context.Products.Find(Id);
      context.Products.Remove(p);
      context.SaveChanges();
    }
  }
}

지금까지 Data Context 클래스, Database Initializer 클래스 및 Repository 클래스를 만들었습니다. 모든 것이 제자리에 있는지 확인하기 위해 인프라 프로젝트를 구축합시다. ProductApp.Infrastructure 프로젝트는 아래 이미지와 같이 표시됩니다.

이제 인프라 프로젝트 생성이 완료되었습니다. 우리는 Infrastructure 프로젝트 내에서 모든 데이터베이스 작업 관련 클래스를 작성했으며 모든 데이터베이스 관련 논리는 중앙 위치에 있습니다. 데이터베이스 로직을 변경해야 할 때마다 인프라 프로젝트만 변경하면 됩니다.

테스트 프로젝트

Repository Pattern의 가장 큰 장점은 테스트 가능성입니다. 이를 통해 프로젝트의 다른 구성 요소에 의존하지 않고 다양한 구성 요소를 단위 테스트할 수 있습니다. 예를 들어, 기능의 정확성을 확인하기 위해 데이터베이스 작업을 수행하는 Repository 클래스를 만들었으므로 단위 테스트를 수행해야 합니다. 또한 웹 프로젝트나 UI에 대한 종속성 없이 Repository 클래스에 대한 테스트를 작성할 수 있어야 합니다. 리포지토리 패턴을 따르고 있기 때문에 MVC 프로젝트(UI)에 대한 종속성 없이 인프라 프로젝트에 대한 단위 테스트를 작성할 수 있습니다.

ProductRepository 클래스에 대한 단위 테스트를 작성하기 위해 테스트 프로젝트에 다음 참조를 추가해 보겠습니다.

  1. ProductApp.Core 프로젝트 참조
  2. ProductApp.Infrastructure 프로젝트 참조
  3. Entity Framework package

Entity Framework를 추가하려면 테스트 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 Nuget 패키지 관리를 클릭합니다. 패키지 관리자 Windows에서 Entity Framework를 검색하고 안정적인 최신 버전을 설치합니다.

ProductApp.Core 프로젝트의 참조를 추가하려면 테스트 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 참조 추가를 클릭합니다. 참조 창에서 프로젝트 탭을 클릭하고 ProductApp.Core를 선택합니다.

ProductApp.Infrastructure 프로젝트의 참조를 추가하려면 테스트 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 참조 추가를 클릭합니다. 참조 창에서 프로젝트 탭을 클릭하고 ProductApp.Infrastructure를 선택합니다.

연결 문자열 복사

Visual Studio는 항상 실행 중인 프로젝트의 구성 파일을 읽습니다. 인프라 프로젝트를 테스트하기 위해 테스트 프로젝트를 실행합니다. 따라서 연결 문자열은 테스트 프로젝트의 App.Config의 일부여야 합니다. 테스트 프로젝트의 인프라 프로젝트에서 연결 문자열을 복사하여 붙여 넣겠습니다.

필요한 모든 참조를 추가하고 연결 문자열을 복사했습니다. 이제 Test 클래스를 설정해 보겠습니다. ProductRepositoryTest라는 이름의 테스트 클래스를 만듭니다. Test Initialize는 테스트가 실행되기 전에 실행되는 함수입니다. 테스트를 실행하기 전에 ProductRepository 클래스의 인스턴스를 만들고 ProductDbInitalize 클래스를 호출하여 데이터를 시드해야 합니다. Test Initializer는 아래 목록과 같이 작성할 수 있습니다.

[TestClass]
public class ProductRepositoryTest {
  ProductRepository Repo;
  [TestInitialize]
  public void TestSetup() {
    ProductInitalizeDB db = new ProductInitalizeDB();
    System.Data.Entity.Database.SetInitializer(db);
    Repo = new ProductRepository();
  }
}

이제 테스트 이니셜라이저를 작성했습니다. 이제 ProductInitalizeDB 클래스가 Product 테이블의 두 행을 시드하는지 여부를 확인하는 첫 번째 테스트를 작성해 보겠습니다. 우리가 실행할 첫 번째 테스트이기 때문에 데이터베이스가 생성되었는지 여부도 확인합니다. 그래서 본질적으로 우리는 테스트를 작성하고 있습니다.

  1. 데이터베이스 생성을 확인하려면
  2. Product Database Initializer의 seed 메소드에 의해 삽입된 행 수를 확인하려면
[TestMethod]
public void IsRepositoryInitalizeWithValidNumberOfData() {
  var result = Repo.GetProducts();
  Assert.IsNotNull(result);
  var numberOfRecords = result.ToList().Count;
  Assert.AreEqual(2, numberOfRecords);
}

보시다시피 Repository GetProducts() 함수를 호출하여 데이터베이스를 생성하는 동안 삽입된 모든 제품을 가져옵니다. 이 테스트는 실제로 GetProducts()가 예상대로 작동하는지 여부를 확인하고 데이터베이스 생성도 확인합니다. 테스트 탐색기 창에서 확인을 위해 테스트를 실행할 수 있습니다.

Repository GetProducts() 함수를 호출하여 모든 제품을 가져옵니다.

테스트를 실행하려면 먼저 테스트 프로젝트를 빌드한 다음 상단 메뉴에서 Test->Windows-Test Explorer를 선택합니다. 테스트 탐색기에서 나열된 모든 테스트를 찾을 수 있습니다. 테스트를 선택하고 실행을 클릭합니다.

계속해서 Repository에서 Add Product 작업을 확인하기 위해 테스트를 하나 더 작성해 보겠습니다.

[TestMethod]
public void IsRepositoryAddsProduct() {
  Product productToInsert =
      new Product { Id = 3, inStock = true, Name = "Salt", Price = 17

      };
  Repo.Add(productToInsert);
  // If Product inserts successfully,
  // number of records will increase to 3
  var result = Repo.GetProducts();
  var numberOfRecords = result.ToList().Count;
  Assert.AreEqual(3, numberOfRecords);
}

제품의 삽입을 확인하기 위해 Repository에서 Add 함수를 호출합니다. 제품이 성공적으로 추가되면 레코드 수가 2개에서 3개로 증가하며 이를 확인하고 있습니다. 테스트를 실행하면 테스트에 통과한 것을 확인할 수 있습니다.

이러한 방식으로 Product Repository 클래스에서 모든 데이터베이스 작업에 대한 테스트를 작성할 수 있습니다. 이제 테스트를 통과했기 때문에 Repository 클래스를 올바르게 구현했다고 확신하며, 이는 Infrastructure 및 Core 프로젝트를 모든 UI (이 경우 MVC) 프로젝트와 함께 사용할 수 있음을 의미합니다.

MVC 또는 웹 프로젝트

드디어 MVC 프로젝트에 도달했습니다! 테스트 프로젝트와 마찬가지로 다음 참조를 추가해야 합니다

  1. ProductApp.Core 프로젝트 참조
  2. ProductApp.Infrastructure 프로젝트 참조

ProductApp.Core 프로젝트의 참조를 추가하려면 MVC 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 참조 추가를 클릭합니다. 참조 창에서 프로젝트 탭을 클릭하고 ProductApp.Core를 선택합니다.

ProductApp.Infrastructure 프로젝트의 참조를 추가하려면 MVC 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 참조 추가를 클릭합니다. 참조 창에서 프로젝트 탭을 클릭하고 ProductApp.Infrastructure를 선택합니다.

연결 문자열 복사

Visual Studio는 항상 실행 중인 프로젝트의 구성 파일을 읽습니다. 인프라 프로젝트를 테스트하기 위해 테스트 프로젝트를 실행하므로 연결 문자열은 테스트 프로젝트의 App.Config의 일부여야 합니다. 더 쉽게 하기 위해 테스트 프로젝트의 인프라 프로젝트에서 연결 문자열을 복사하여 붙여 넣겠습니다.

응용 프로그램 발판

MVC 컨트롤러를 스캐폴딩할 수 있는 모든 것을 갖추어야 합니다. 스캐폴드하려면 컨트롤러 폴더를 마우스 오른쪽 단추로 클릭하고 아래 이미지와 같이 Entity Framework를 사용하여 MVC 5 Controller with Views를 선택합니다.

응용 프로그램 발판

다음으로 컨트롤러 추가 창이 나타납니다. 여기서 모델 클래스 및 데이터 컨텍스트 클래스 정보를 제공해야 합니다. 이 프로젝트에서 모델 클래스는 Core 프로젝트의 Product 클래스이고 Data 컨텍스트 클래스는 Infrastructure 프로젝트의 ProductDataContext 클래스입니다. 아래 이미지와 같이 드롭다운에서 두 클래스를 모두 선택하겠습니다.

여기서 모델 클래스 및 데이터 컨텍스트 클래스 정보를 제공해야 합니다

또한 Generate Views, Reference script libraries 및 Use a layout page 옵션이 선택되어 있는지 확인해야 합니다.

추가를 클릭하면 Visual Studio에서 Views/Products 폴더 내에 ProductsController 및 Views를 만듭니다. MVC 프로젝트는 아래 이미지와 같은 구조를 가져야 합니다.

Visual Studio는 Views/Products 폴더 내에 ProductsController 및 Views를 만듭니다.

이 시점에서 응용 프로그램을 실행하면 Product 엔터티에서 CRUD 작업을 수행할 수 있습니다.

  go ahead and run the application,

비계 문제

그러나 우리는 아직 끝나지 않았습니다! ProductsController 클래스를 열고 코드를 살펴보겠습니다. 첫 번째 줄에서 문제를 찾을 수 있습니다. MVC 스캐폴딩을 사용했기 때문에 MVC는 데이터베이스 작업을 수행하기 위해 ProductContext 클래스의 개체를 만듭니다.

컨텍스트 클래스에 대한 모든 종속성은 UI 프로젝트와 데이터베이스를 서로 밀접하게 바인딩합니다. 아시다시피 Datacontext 클래스는 Entity Framework 구성 요소입니다. MVC 프로젝트가 인프라 프로젝트에서 어떤 데이터베이스 기술이 사용되고 있는지 알기를 원하지 않습니다. 반면에 Datacontext 클래스는 테스트하지 않았습니다. ProductRepository 클래스를 테스트했습니다. 이상적으로는 ProductContext 클래스 대신 ProductRepository 클래스를 사용하여 MVC 컨트롤러에서 데이터베이스 작업을 수행해야 합니다.  요약하자면,

  1. MVC 스캐폴딩은 데이터 컨텍스트 클래스를 사용하여 데이터베이스 작업을 수행합니다. 데이터 컨텍스트 클래스는 Entity Framework 구성 요소이므로 MVC(UI)와 EF(데이터베이스) 기술을 밀접하게 결합하는 데 사용됩니다.
  2. 데이터 컨텍스트 클래스는 단위 테스트를 거치지 않았으므로 사용하는 것은 좋지 않습니다.
  3. 테스트 된 ProductRepository 클래스가 있습니다. 데이터베이스 작업을 수행하기 위해 컨트롤러 내부에서 이것을 사용해야 합니다. 또한 ProductRepository 클래스는 데이터베이스 기술을 UI에 노출하지 않습니다.

데이터베이스 작업에 ProductRepository 클래스를 사용하려면 ProductsController 클래스를 리팩토링해야 합니다. 이를 위해 따라야 할 두 가지 단계가 있습니다.

  1. ProductContext 클래스 대신 ProductRepository 클래스의 객체를 만듭니다.
  2. ProductContext 클래스의 메소드 대신 Product 엔티티에 대한 데이터베이스 작업을 수행하려면 ProductRepository 클래스의 메소드를 호출하십시오.

아래 목록에서는 ProductContext를 사용하여 코드에 주석을 달고 ProductRepository 메소드를 호출했습니다. 리팩토링 후 ProductController 클래스는 다음과 같습니다.

using System;
using System.Net;
using System.Web.Mvc;
using ProductApp.Core;
using ProductApp.Infrastructure;

namespace ProductApp.Web.Controllers {
  public class ProductsController : Controller {
    // private ProductContext db = new ProductContext();
    private ProductRepository db = new ProductRepository();

    public ActionResult Index() {
      // return View(db.Products.ToList());
      return View(db.GetProducts());
    }

    public ActionResult Details(int? id) {
      if (id == null) {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
      }
      // Product product = db.Products.Find(id);
      Product product = db.FindById(Convert.ToInt32(id));
      if (product == null) {
        return HttpNotFound();
      }
      return View(product);
    }

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

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(
        [Bind(Include = "Id,Name,Price,inStock")] Product product) {
      if (ModelState.IsValid) {
        // db.Products.Add(product);
        // db.SaveChanges();
        db.Add(product);
        return RedirectToAction("Index");
      }

      return View(product);
    }

    public ActionResult Edit(int? id) {
      if (id == null) {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
      }
      Product product = db.FindById(Convert.ToInt32(id));
      if (product == null) {
        return HttpNotFound();
      }
      return View(product);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(
        [Bind(Include = "Id,Name,Price,inStock")] Product product) {
      if (ModelState.IsValid) {
        // db.Entry(product).State = EntityState.Modified;
        // db.SaveChanges();
        db.Edit(product);
        return RedirectToAction("Index");
      }
      return View(product);
    }

    public ActionResult Delete(int? id) {
      if (id == null) {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
      }
      Product product = db.FindById(Convert.ToInt32(id));
      if (product == null) {
        return HttpNotFound();
      }
      return View(product);
    }

    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public ActionResult DeleteConfirmed(int id) {
      // Product product = db.FindById(Convert.ToInt32(id));
      //  db.Products.Remove(product);
      //  db.SaveChanges();
      db.Remove(id);
      return RedirectToAction("Index");
    }

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

리팩토링 후 응용 프로그램을 빌드하고 실행해 보겠습니다 – 그렇게 하고 CRUD 작업을 수행할 수 있어야 합니다.

종속성 주입

이제 응용 프로그램이 실행 중이며 리포지토리 패턴을 사용하여 만들어졌다는 사실에 만족합니다. 그러나 여전히 문제가 있습니다 : 우리는 ProductsController 클래스 내에 ProductRepository 클래스의 객체를 직접 생성하고 있으며 이것을 원하지 않습니다. 우리는 종속성을 반전시키고 종속성을 주입하는 작업을 일반적으로 DI 컨테이너로 알려진 제3자에게 위임하려고 합니다. 기본적으로 ProductsController 는 DI 컨테이너에 IProductRepository 의 인스턴스를 반환하도록 요청합니다.

MVC 애플리케이션에 사용할 수 있는 많은 DI 컨테이너가 있습니다. 이 예제에서는 가장 간단한 Unity DI 컨테이너를 사용합니다. 이렇게 하려면 MVC 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 Nuget 패키지 관리를 클릭합니다. NuGet 패키지 관리자에서 Unity.MVC를 검색하고 패키지를 설치합니다.

Nuget 패키지 관리

Unity.Mvc 패키지가 설치되면 App_Start 폴더를 열어 보겠습니다. App_Start 폴더 내에서 UnityConfig.cs 파일을 찾을 수 있습니다. UnityConfig 클래스에서 타입을 등록해야 합니다. 이렇게 하려면 UnityConfig 클래스에서 RegisterTypes 함수를 열고 아래 목록과 같이 타입을 등록합니다.

public static void RegisterTypes(IUnityContainer container) {
  // TODO: Register your types here
  container.RegisterType<iproductrepository, productrepository = "">();
}

Unity DI 컨테이너에 타입을 등록했습니다. 이제 ProductsController 클래스에서 약간의 리팩토링을 수행해 보겠습니다.  ProductsController의 생성자에서 리포지토리 인터페이스에 대한 참조를 전달합니다. 애플리케이션에서 필요할 때마다 Unity DI 컨테이너는 타입을 확인하여 ProductRepository의 구체적인 오브젝트를 애플리케이션에 주입합니다. 아래 목록과 같이 ProductsController를 리팩토링해야 합니다.

public class ProductsController : Controller {
  IProductRepository db;
  public ProductsController(IProductRepository db) {
    this.db = db;
  }

계속해서 응용 프로그램을 빌드하고 실행해 보겠습니다. 응용 프로그램을 실행하고 실행해야하며 Repository Pattern 및 Dependency Injection을 사용하여 CRUD 작업을 수행 할 수 있어야합니다!

결론

이 기사에서는 리포지토리 패턴에 따라 MVC 애플리케이션을 만드는 방법을 단계별로 배웠습니다. 이렇게 하면 모든 데이터베이스 논리를 한 곳에 넣을 수 있으며 필요할 때마다 리포지토리를 변경하고 테스트하기만 하면 됩니다. 또한 Repository Pattern은 응용 프로그램 UI를 Database logic 및 Domain 엔터티와 느슨하게 결합하여 응용 프로그램을 더 쉽게 테스트할 수 있도록 합니다.

또한 HTML5, Angular, React 또는 ASP.NET MVC와 함께 사용하여 풍부한 인터넷 응용 프로그램을 만들 수 있는 Ignite UI 확인하는 것을 잊지 마십시오. 읽어 주셔서 감사합니다!

데모 요청