솔리드 원리 적용에 도움이 필요함

asp.net-mvc dapper entity-framework n-tier-architecture oop

문제

Juile Lerman의 "기업에서의 EF"에 대한 복수형 과정에 깊은 인상을 받았으며 데모 앱을 제작하기로 결정했습니다.

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

나는이 데모 애플리케이션을 위해 DB 첫 접근 방식을 사용했다. 여기에는 UserDetails라는 하나의 테이블 만 포함되며 SQL 서버에서 어떻게 보이는지 아래에 나와 있습니다. 이 테이블을 CRUD 작업에 사용하겠습니다. 여기에 이미지 설명을 입력하십시오.

다음은 내 애플리케이션을 계층화 한 것입니다.

1. WESModel 솔루션 : 이 레이어에는 다음과 같이 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에 대한 참조를 포함합니다. 이 레이어는 아래와 같이 My Repository 클래스를가집니다. 당분간은 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와 그것을 교환 할 수 있어야하는 그런 방식으로이 프로젝트에서 그것을 사용하고 싶다. 어떻게 이것을 달성합니까?

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

수락 된 답변

질문 : 내 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을 사용하십시오.


인기 답변

명시 적 변수 유형 지정 대신 암시 적 변수 유형 지정 (즉, 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 등)를 사용하여 의존성을 자동으로 해결하는 연습 인 Inversion of Control (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>();
            });
        }
    }
}


아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.