3

"EF in Enterprise"에 대한 Juile Lerman의 복수형 과정에 정말 감명 받았고 데모 응용 프로그램을 구축하기로 결정했습니다.SOLID 원칙을 적용 할 때 도움이 필요합니다

VS 2012와 최신 버전의 EF, SQL Server 및 MVC를 사용하고 있습니다. 솔리드 (SOLID) 원칙을 적용하는 데모 애플리케이션을 제작 중입니다. DI & 단위 테스트를 구현하는 방법을 더 잘 이해하기 위해이 작업을 수행하고 있습니다.

저는이 데모 응용 프로그램에 대해 DB 첫 번째 접근 방식을 사용했습니다. 여기에는 UserDetails라는 하나의 테이블 만 포함되며 SQL 서버에서 어떻게 보이는지 아래에 나와 있습니다. 이 테이블을 CRUD 작업에 사용하겠습니다.

1. WESModel 해결 방법 : 아래 enter image description here

내가 내 응용 프로그램을 적층 한 방법이다이 층 내 Model1.edmx 파일을 다음과 같이 상황에 맞는 클래스가 포함되어 있습니다.

namespace WESModel 
{ 
    using System; 
    using System.Data.Entity; 
    using System.Data.Entity.Infrastructure; 
    using WESDomain; 

    public partial class WESMVCEntities : DbContext 
    { 
     public WESMVCEntities() 
      : base("name=WESMVCEntities") 
     { 
     } 

     protected override void OnModelCreating(DbModelBuilder modelBuilder) 
     { 
      throw new UnintentionalCodeFirstException(); 
     } 

     public DbSet<UserDetail> UserDetails { get; set; } 
    } 
} 

2 WESDomain 솔루션 :이 층 내 도메인 클래스 (또는 POCO 클래스)가 포함되어 있습니다. 이 POCO 클래스는 실제로 내 WESModel 레이어에서 자동 생성되었습니다. 나는 그들을이 층으로 옮겼다. 다음은 단일 POCO 클래스의 모습입니다.

namespace WESDomain 
{ 
    using System; 
    using System.Collections.Generic; 

    public partial class UserDetail:IUserDetail 
    { 
     public int Id { get; set; } 
     public string UserName { get; set; } 
    } 
} 

3 WESDataLayer 해결책 : 층이 제 2 층으로부터 상기 DLL에 대한 참조를 포함한다. 이 레이어에는 아래에 표시된 것과 같은 리포지토리 클래스가 있습니다. 지금, 나는 같은 클래스의 IRepository를 유지하고있다 :)

namespace WESDataLayer 
{ 
    public class UserDetailRepository : IUserDetailRepository 
    { 
     WESMVCEntities context = new WESMVCEntities(); 

     public IQueryable<IUserDetail> All 
     { 
      get { return context.UserDetails; } 
     } 

     public IQueryable<IUserDetail> AllIncluding(params Expression<Func<IUserDetail, object>>[] includeProperties) 
     { 
      IQueryable<IUserDetail> query = context.UserDetails; 
      foreach (var includeProperty in includeProperties) { 
       query = query.Include(includeProperty); 
      } 
      return query; 
     } 

     public IUserDetail Find(int id) 
     { 
      return context.UserDetails.Find(id); 
     } 

     public void InsertOrUpdate(UserDetail userdetail) 
     { 
      if (userdetail.Id == default(int)) { 
       // New entity 
       context.UserDetails.Add(userdetail); 
      } else { 
       // Existing entity 
       context.Entry(userdetail).State = EntityState.Modified; 
      } 
     } 

     public void Delete(int id) 
     { 
      var userdetail = context.UserDetails.Find(id); 
      context.UserDetails.Remove(userdetail); 
     } 

     public void Save() 
     { 
      context.SaveChanges(); 
     } 

     public void Dispose() 
     { 
      context.Dispose(); 
     } 
    } 

    public interface IUserDetailRepository : IDisposable 
    { 
     IQueryable<IUserDetail> All { get; } 
     IQueryable<IUserDetail> AllIncluding(params Expression<Func<UserDetail, object>>[] includeProperties); 
     UserDetail Find(int id); 
     void InsertOrUpdate(UserDetail userdetail); 
     void Delete(int id); 
     void Save(); 
    } 
} 

4 :의 ConsoleApplication1 솔루션 : 이것은 내 UI 계층입니다. 내 최종 애플 리케이션에서 내 MVC 응용 프로그램이 될 것입니다. 여기서는 단순히 DB에 쿼리하고 데이터를 표시합니다. 이것은 코드가 어떻게 보이는지입니다.

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IUserDetailRepository repo = new UserDetailRepository(); 

      var count = repo.All.ToList().Count().ToString(); 
      Console.WriteLine("Count: {0}", count); 
      Console.ReadLine(); 

     } 
    } 
} 

질문 : 내 UI 층은 EF DLL에 대한 심판이 없습니다. 그러나 Repository 클래스의 인스턴스가 있습니다. MVC 응용 프로그램에서 내 컨트롤러는 저장소 클래스 또는 UnitOfWork의 인스턴스를 갖습니다.

a) 올바른 방법인가?

b) 내가 추상화 할 수있는 방법이 있습니까?

c) 미래에 Dapper 나 다른 ORM 도구로 EF를 바꾸려면 어떻게해야합니까?

d) DI 프로젝트를이 프로젝트에 어떻게 맞 춥니 까? 어느 층에 있어야합니까?

e) 단위 테스트. 나는 StructureMap에 대해 알고 있고, 미래에 내가 Ninject와 그것을 교환 할 수 있어야하는 그런 방식으로이 프로젝트에서 그것을 사용하고 싶다. 어떻게 이것을 달성합니까?

이 큰 질문을 읽어 주셔서 감사합니다. 누군가 올바른 방향으로 나를 가리킬 수 있다면 정말 고마워요.

+0

'IRepository'는'IQueryable' 타입을 반환 할 필요가 없습니다. 제발, [이 링크를 참조하십시오] (https://stackoverflow.com/questions/33755499/entity-framework-repository-pattern-why-not-return-iqueryable) – StepUp

답변

3

질문 : 내 UI 층은 EF DLL에 대한 심판이 없습니다. 그러나 Repository 클래스의 인스턴스는 입니다. MVC 응용 프로그램에서 내 컨트롤러 에는 저장소 클래스 또는 UnitOfWork의 인스턴스가 있습니다.

예, UI 레이어 클래스에는 EF에 대한 참조가 없어야합니다. 하지만 이렇게하려면 구체적인 저장소에 대한 참조를 가질 수 없습니다. MVC 응용 프로그램에서 서비스 계층을 사용하지 않으면 Controller는 IUserDetailRepository에 대한 참조 만 가지며 구체적인 유형의 생성을 기다립니다. UnitOfWork에 대한 내용은 구현에 따라 다릅니다. :-)

a) 올바른 방법입니까?

옳은 일은 "느슨한 커플 링 (loose coupling)"이라고하며, 디자인이 이런 식으로 선택하는 것 같습니다.

b) 내가 추상화 할 수있는 방법이 있습니까?

예, 종속성 해결 프로그램을 사용할 수 있습니다. 이렇게하면 구체적인 유형을 참조 할 필요가 없습니다. 추상화에만 기반한 코드를 갖게됩니다.

c) 미래에 Dapper 또는 다른 ORM 도구로 EF를 스왑하려는 경우 어떻게해야합니까?

데이터 액세스 레이어 (예 : IXxxRepository 계약의 구체적인 구현을 포함하는 라이브러리)가 있어야합니다. 귀하의 경우에는 EF 구현이 될 것입니다. Dapper에서 변경하면이 레이어를 다시 구현해야합니다. 리팩토링에는 허용되는 한도가 있습니다.

d) DI 프로젝트를이 프로젝트에 어떻게 맞추나요? 어느 층에 있어야합니까?

DI 도구를 배치하기 가장 좋은 장소는 UI 레이어입니다. 응용 프로그램 시작시 종속성 바인딩을 구성하면 모든 것이 자동으로 작동합니다.)

e) 단위 테스트. 나는 StructureMap에 대해 알고 있고, 미래에 내가 Ninject와 그것을 교환 할 수 있어야하는 그런 방식으로이 프로젝트에서 그것을 사용하고 싶다. 어떻게 이것을 달성합니까?

다른 것을 연결하기 위해 종속성 해결 프로그램의 연결을 해제 하시겠습니까? 문제가되지 않습니다. DR 구성을 코딩 할 때 응용 프로그램과의 최소 결합을 예측할 수 있습니다. 어떤 경우에 커플 링을 제한하는 몇 가지 팁이 있습니다 ... 현재 작업하고있는 프로젝트에서 MVC 애플리케이션과 서비스 레이어, 비즈니스 레이어, 데이터 액세스 레이어 및 인프라 레이어가 있습니다. 우리는 Ninject를 DR로 사용하며, Infrastructure와 Web UI 레이어 만이 Ninject에 대한 참조를 가지고 있습니다. 플러그를 뽑는 것은 매우 쉽습니다. 우리는 이미이 방법으로 Unity를 시도했습니다.

한 가지 더, 당신은 UserDetail에 대한 계약이 없어야합니다. 그럴 필요가 없습니다. DTO와 같은 모든 클래스보다는 상태없는 클래스에서 Dependency Injection을 사용하십시오.

+0

와우 ... 좋은 설명. 이제는 1에 대한 정보가 더 필요합니다. 의존성 리졸버 사용을 가리키는 링크가 있습니까? 2. 결국 당신은 "UserDetail에 대한 계약이 없어야한다"고 말했다. 정확히 어떤 층을 언급하고 있습니까? 시간과 자세한 답변을 보내 주셔서 감사합니다. – NoobDeveloper

+0

나는 귀하의 도메인 계층에서 UserDetail 클래스와 IUserDetail 계약을 참조하고 있습니다. 나에게 그것은 비즈니스 오브젝트에 대한 계약을 작성하는 아키텍처 (또는 여러 가지 구현이있을 수 있음) 이상입니다. – Julien

+0

일반 링크에서 종속성 해결 프로그램을 사용 하시겠습니까? 난 그냥 Ninject의 사용에 대한 링크가 :)하지만 예제에서, 당신은 DR을 사용하여 전반적인 아키텍처를 이해할 것입니다 ... 이것을 시도하십시오 http://stefanoricciardi.com/2011/01/21/ninject-mini-tutorial-part -1/ – Julien

0

간단히 :

이 모형은 IRepository에 종속

등의 모델이 있으며, 쿼리를 수행, 데이터를 유지하기 위해 (IRepository의 구현은 아무것도, 단정 한, EF, ADO.Net 등이 될 수 있음) 비즈니스 규칙.

로보기 (콘솔, WPF, 웹), 뷰와 모델 사이의 계층에 대한 종속성이 중 하나 presenter (MVP), controller (MVC) 또는 viewmodel (MVVM).

중간 계층은 데이터를 유지하기 위해 모델과 함께 작동합니다.

DI를 사용하여 종속성 지점을 사용할 수 있습니다.

단위 테스트는 모든 레이어에 적용 할 수 있지만 특별히 비즈니스 규칙을 다루는 지 확인하십시오.

IUserDetailRepository은 저장소와 모양이 같아서 모델에서 사용하고 있어야합니다. 이렇게하면 인터페이스를 추상화하여 데이터베이스 구현을 분리 할 수 ​​있습니다. 앞에서 설명한 것처럼 실제 구현은 EF, 대퍼 등일 수 있습니다.

MVC 모델에서 컨트롤러는 비즈니스 규칙을 적용하기 위해 모델을 호출합니다 데이터를 지속시킵니다.

+0

나는 더 많은 설명이 필요합니다. 당신은 내 모델이 IRepository에 대한 절망감을 가지고 있다고 말했다. 위 코드에서 WESDataLayer Solution 프로젝트를 언급하고 있습니까? 2. MVC 앱의 경우 컨트롤러가 데이터를 어떻게 쿼리해야합니까?위 코드에서 Controller와 WESDataLayer Solution 사이에 다른 레이어를 추가해야합니까? 실제로 제가 위에서 말한이 "모델"이 정확히 무엇인지 혼란 스럽습니다. 내 응용 프로그램의 관점에서 "모델"은 무엇입니까? 시간 내 줘서 고마워. – NoobDeveloper

2

명시 적 변수 입력 (즉, var 키워드 제거) 대신 암시 적 변수 입력을 사용하면 훨씬 쉽게 종속성을 확인할 수 있습니다. 가능하다면 클래스 (UserDetailRepository) 사용에 비해 인터페이스 (IUserDetailRepository)의 사용을 선호하십시오.

예 :

1) 컴파일러

var repo = new UserDetailRepository();

2) 인터페이스

결정 타입 클래스 참조

UserDetailRepository repo = new UserDetailRepository();

3)에 의해 결정된 입력 유형을 판별 할 수 있도록

IUserDetailRepository repo = new UserDetailRepository();

컴파일러가 아닌 인터페이스에 따라 유형을 결정할 수 있으므로 동일한 인터페이스 (예 : IUserDetailRepository repo = new DapperUserDetailRepository();

또한, 당신은 이에 당신을 자동으로 의존성을 해결하기 위해 특정 IoC 컨테이너 (Ninject, CastleWinsor, Unity 등)을 사용하는 관행이 제어의 반전 (IOC의)라는 원칙의 경계에있는 new 키워드를 직접 호출하지 마십시오. 당신이 StructureMap을 언급 이후

는 여기에 작동하는 방법의 예 :

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      IContainer container = ConfigureDependencies(); 
      IUserDetailRepository repo = container.GetInstance<IUserDetailRepository>(); 

      var count = repo.All.ToList().Count().ToString(); 
      Console.WriteLine("Count: {0}", count); 
      Console.ReadLine(); 

     } 

     private static IContainer ConfigureDependencies() { 
      return new Container(x =>{ 
       x.For<IUserDetailRepository>().Use<UserDetailRepository>(); 
      }); 
     } 
    } 
} 
+0

사실, 귀하의 답변에 언급 한 내용과 정확히 동일하게 수행했습니다. 동일한 내용을 반영하도록 원본 게시물을 업데이트했습니다. 2 가지 질문이 거의 없습니다. 나는 StructureMap에 대해 알고 있고, 미래에 내가 Ninject와 그것을 교환 할 수 있어야하는 그런 방식으로이 프로젝트에서 그것을 사용하고 싶다. 어떻게 이것을 달성합니까? – NoobDeveloper

+0

Andrew에게 다시 한 번 감사드립니다. 혼란 스럽습니다. 나는 더 분명해야했다. 나는 "어떻게"StructureMap을 사용하는지 알고 있습니다. 샘플 코드는 UI 레이어/MVC 응용 프로그램의 컨트롤러에서 사용할 수있는 가장 간단한 방법입니다. 그러나, 내가 그것을 추상화 할 수있는 방법을 알고 싶습니다/느슨하게 그것을 UI/컨트롤러와 연결할 수 있으므로 필요한 경우 Ninject로 바꿀 수 있습니다. – NoobDeveloper

+0

저는 DI 컨테이너에 대한 완전한 전문가는 아니지만 제 경험상 DI 컨테이너는 앱과 밀접하게 결합되어있어 효과적입니다. DI 컨테이너를 추상화하려는 시도는 실제 구현에서 차이점이있는 것보다 더 큰 문제 일 가능성이 큽니다. – Claies

관련 문제