3

작업 단위 및 저장소 패턴에 대한 복수 자습서를 수행했습니다. MVC와 Ef4 구현에 함께 두 가지를 넣으려고합니다. 이 자습서에는 작은 EF 모델이 있으며 두 개의 엔티티 만 있습니다. 따라서 2 개의 엔티티와 일치하는 객체는 네비게이션 속성 등의 ICollection으로 정의되었습니다. 처음에는 코드에서와 같지만 객체와 엔티티 프레임 워크 사이에는 아무런 연관이 없었습니다. 이들은 작업 단위 (UOW)에서 저장소로 집계 된 오브젝트입니다. 그러나 이것이 내가 다음 질문을하기 시작하는 곳입니다.저장소 패턴 + 작업 단위 패턴 + MVC3 + EF4 - 모든 것과 함께 작동하는 모델 데이터 정의 문제

  1. 엔티티 프레임 워크에서 개체를 사용할 수있을 때 엔티티 프레임 워크에 이미있는 개체의 기본적으로 중복 개체를 만드는 이유는 무엇입니까? 안 그래?
  2. 이러한 개체를 만드는 경우 전체 ef 모델을 매핑해야합니까? 탐색 속성에 대한 ICollections가 있으면 개체가 정확히 일치하지 않고 기본적으로 대부분의 모델에서 연속적으로 오류가 발생합니다. 이 모델은 12 개가 넘는 엔티티가 포함 된 상당히 크기 때문에 저장소에서 사용되지 않는 엔티티를 생성하지 않아도됩니다.
  3. 이러한 클래스가 뷰에 보내는 클래스 인 경우 실제 모델 클래스의 버전을 제거해야하는 뷰 모델 클래스가 아닌가요? 이는 내 컨트롤러에서 작업 단위 및 저장소 클래스를 사용하여 데이터를 검색 한 다음 해당 데이터를 제거하고 특정 데이터 조각 만 뷰 모델에 전달한다는 것을 의미합니다. 이제는 작업 단위와 저장소 패턴을 사용하여 방금 얻은 모든 청결 상태가 다시 사라지기 시작한다는 것을 의미합니다.

나는 ...

namespace Data 
{ 

    public interface IRepository<T> 
       where T : class//, IEntity 
    { 
     IQueryable<T> FindAll(); 
     IQueryable<T> Find(Expression<Func<T, bool>> predicate); 
     //T FindById(int id); 
     void Add(T newEntity); 
     void Remove(T entity); 
    } 

    public class SqlRepository<T> : IRepository<T> 
           where T : class//, IEntity 
    { 

     public SqlRepository(ObjectContext context) 
     { 
      _objectSet = context.CreateObjectSet<T>(); 
     } 

     public IQueryable<T> Find(Expression<Func<T, bool>> predicate) 
     { 
      return _objectSet.Where(predicate); 
     } 

     public void Add(T newEntity) 
     { 
      _objectSet.AddObject(newEntity); 
     } 

     public void Remove(T entity) 
     { 
      _objectSet.DeleteObject(entity); 
     } 


     public IQueryable<T> FindAll() 
     { 
      return _objectSet; 
     } 

     protected ObjectSet<T> _objectSet; 
    } 

} 



public class SqlUnitOfWork : IUnitOfWork 
{ 

    public SqlUnitOfWork() 
    { 
     var connectionString = 
      ConfigurationManager 
       .ConnectionStrings[ConnectionStringName] 
       .ConnectionString; 

     _context = new ObjectContext(connectionString); 
     _context.ContextOptions.LazyLoadingEnabled = true; 
    } 

    public IRepository<Domain.Project> Projects 
    { 
     get 
     { 
      if (_projects == null) 
      { 
       _projects = new SqlRepository<Domain.Project>(_context); 
      } 
      return _projects; 
     } 
    } 


    public void Commit() 
    { 
     _context.SaveChanges(); 
    } 

    SqlRepository<Domain.Project> _projects = null; 
    //SqlRepository<TimeCard> _timeCards = null; 
    readonly ObjectContext _context; 
    const string ConnectionStringName = "Entities"; 
} 
} 



namespace Domain 
{ 
    public interface IRepository<T> 
       where T : class//, IEntity 
    { 
     IQueryable<T> FindAll(); 
     IQueryable<T> Find(Expression<Func<T, bool>> predicate); 
     //T FindById(int id); 
     void Add(T newEntity); 
     void Remove(T entity); 
    } 


    public interface IUnitOfWork 
    { 
     IRepository<Project> Projects { get; } 
     //IRepository<TimeCard> TimeCards { get; } 
     void Commit(); 
    } 
} 

를 다음 코드가 다음 나는 또한 도메인 네임 스페이스에 ... 다음과 같은 방식으로 내 도메인 클래스를 선언했습니다.

public class Project //: IEntity 
{ 
    public virtual int ProjectId { get; set; } 
    public virtual int UserId { get; set; } 
    public virtual int CategoryId { get; set; } 
    public virtual string Name { get; set; } 
    public virtual string Description { get; set; } 
    // nav prop - do I have to have these? 
    public virtual ICollection<Image> Images { get; set; } 
    public virtual ICollection<WatchedProject> WatchedProjects { get; set; } 
    public virtual ICollection<RequestForProposal> RequestForProposals { get; set; } 
    public virtual ICollection<Message> Messages { get; set; } 
    public virtual Category Category { get; set; } 
} 

는 지금은 그냥이 테스트 그리고 난 다음 콘솔 응용 프로그램 코드가 ...

static void Main(string[] args) 
    { 
     IUnitOfWork _unitOfWork = new SqlUnitOfWork(); 
     IRepository<Domain.Project> _repository = _unitOfWork.Projects; 

     var prjs = _unitOfWork.Projects.FindAll(); 


     foreach (Domain.Project prj in prjs) 
     { 
      Console.WriteLine(prj.Description); 
     } 

     Console.Read(); 
    } 

을하지만이 줄을 실행하면

IRepository<Domain.Project> _repository = _unitOfWork.Projects; 

의에 ..

_objectSet = context.CreateObjectSet<T>(); 

그리고 나는 ... 예외 때문에

System.Data.MetadataException was unhandled 
    Message=Schema specified is not valid. Errors: 
The relationship 'Model.fk_projects_catetegoryid_categories' was not loaded because the type 'Model.Category' is not available. 
The following information may be useful in resolving the previous error: 
The required property 'ServiceProviderCategories' does not exist on the type 'Domain.Category'. 


The relationship 'Model.FK_Messages_Projects_ProjectId' was not loaded because the type 'Model.Message' is not available. 
The following information may be useful in resolving the previous error: 
The required property 'MessageId' does not exist on the type 'Domain.Message'. 


The relationship 'Model.fk_requestforproposals_projectid_projects' was not loaded because the type 'Model.RequestForProposal' is not available. 
The following information may be useful in resolving the previous error: 
The required property 'Project' does not exist on the type 'Domain.RequestForProposal'. 


The relationship 'Model.FK_WatchedProject_ProjectId_Projects' was not loaded because the type 'Model.WatchedProject' is not available. 
The following information may be useful in resolving the previous error: 
The required property 'WatchedProjectId' does not exist on the type 'Domain.WatchedProject'. 


The relationship 'Model.ProjectImage' was not loaded because the type 'Model.Image' is not available. 
The following information may be useful in resolving the previous error: 
The required property 'ImageId' does not exist on the type 'Domain.Image'. 


    Source=System.Data.Entity 
    StackTrace: 
     at System.Data.Metadata.Edm.ObjectItemCollection.LoadAssemblyFromCache(ObjectItemCollection objectItemCollection, Assembly assembly, Boolean loadReferencedAssemblies, EdmItemCollection edmItemCollection, Action`1 logLoadMessage) 
     at System.Data.Metadata.Edm.ObjectItemCollection.ImplicitLoadAssemblyForType(Type type, EdmItemCollection edmItemCollection) 
     at System.Data.Metadata.Edm.MetadataWorkspace.ImplicitLoadAssemblyForType(Type type, Assembly callingAssembly) 
     at System.Data.Objects.ObjectContext.GetTypeUsage(Type entityCLRType) 
     at System.Data.Objects.ObjectContext.GetEntitySetFromContainer(EntityContainer container, Type entityCLRType, String exceptionParameterName) 
     at System.Data.Objects.ObjectContext.GetEntitySetForType(Type entityCLRType, String exceptionParameterName) 
     at System.Data.Objects.ObjectContext.CreateObjectSet[TEntity]() 
     at Data.SqlRepository`1..ctor(ObjectContext context) in C:\Projects\TestProjectClasses\Data\SqlRepository.cs:line 18 
     at Data.SqlUnitOfWork.get_Projects() in C:\Projects\TestProjectClasses\Data\SqlUnitOfWork.cs:line 31 
     at TestProjectClasses.Program.Main(String[] args) in C:\Projects\TestProjectClasses\TestProjectClasses\Program.cs:line 26 
     at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 
     at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 
     at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
     at System.Threading.ThreadHelper.ThreadStart_Context(Object state) 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
     at System.Threading.ThreadHelper.ThreadStart() 
    InnerException: 

해당 개체가 내 도메인 네임 스페이스에 생성되지 않았기 때문에 나는이 예외가 ...하지만 모든 객체의 복사본을 만들 싶지 않아

. 나는 약간의 연구를했지만 모든 연구를했다. 그러나 나는이 객체들을 어떤 시점에서 변환해야 할 필요가 있다고 생각합니다 ... 컨트롤러 코드에 많은 변환 논리를 넣는 것보다 더 나은 방법이 있습니까? EF에 정의

+1

리포지토리와 작업 단위 패턴으로 이동하기 전에 EF에 대한 이해를 해시 할 수 있습니다. 간단한 모델, 아마도 두 개의 관련 엔티티 만 사용하고 EF가 작동하는 방식과 진행 상황에 대한 느낌을 얻으십시오. 데이터 어노테이션 및 EF 모델 생성을 도와주는 방법에 대해 읽어보십시오. 또한 데이터 컨텍스트가있는 모든 클래스가 IDisposable을 구현해야하는 이유를 조사하십시오. 그런 다음 뷰 모델에 대한 많은 질문을 살펴볼 수 있습니다. 이러한 몇 가지 문제의 세부 사항을 살펴 보는 대신 더 많은 연구를해야한다는 전반적인 필요성을 강조하십시오. –

+0

저는이 기술에 대한 전문가가 아니지만 조금만 노력해 봤습니다. pluralsight 자습서는 이러한 모든 동일한 기술로 만들어졌습니다. 이 튜토리얼은 엔티티 프레임 워크에 묶이지 않은 엔티티의 사본을 만들기에 충분한 시간을 보냈습니다. 그들은 데이터베이스에서 모델을 생성하고 코드를 먼저 사용하지 않았습니다. poco 클래스는 단순히 ... poco 클래스였습니다 ... EF가 사용할 모든 것을 함께 묶을 DBContext는 없었습니다. 그들은 시각적 모델과 디자이너를 사용했습니다. 그래서 저는 왜 그 길이로 가서 복사본을 만들고 그 같은 방법으로 ef 등을 모방하려고합니다. –

+0

@TravisJ UnitOfWork가 엔티티 자체를 직접 사용할 수있게 만들었 기 때문에 필연적이지는 않습니다. 그러나 전체 모델의 복사 클래스를 만드는 데 어떤 이유가 있었는지 알고 싶습니다. –

답변

4
  1. 엔티티는 그들이 어떤 구조로 저장되는 데이터 모델, 지속성 모델 있습니다. 응용 프로그램에는 대개 실제 비즈니스 문제와 솔루션을 모델링하는 모델이 있습니다. 저장소는이 app 객체를받은 다음 저장소 요구 사항에 필요한 정보를 추출합니다.대부분의 경우 두 가지 모델 (앱 및 지속성)이 닮아 있습니다. 단순한 것은 동일하지만 대부분의 경우 의도가 다르기 때문에 차이가 있습니다 (앱 모델 의도는 동작을 모델링하고 지속성 의도는 저장을 모델링하는 것입니다).

    저장된 데이터를로드 할 때 저장소는 저장된 양식에서 앱 개체를 다시 생성하므로 지속성 모델을 앱 모델에 매핑합니다. 모델이 매우 단순하다면 직접 EF 엔티티를 사용할 수 있습니다. 그러나 앞으로 모델이 달라질 것이라는 것을 알고 있다면 처음부터 '변환'을하는 것이 더 안전합니다. 그러면 엔티티를 복제하는 것처럼 보일 것입니다 .

    또한 아이디어는 db 액세스 구현 세부 정보와 EF 엔티티를 구분하는 것입니다. 구현 세부 사항입니다. 내일 EF sux와 Nhibernate가 더 좋다고 결정한 경우 또는 훨씬 가볍고 효과적인 방법이 필요한 경우 나머지 앱을 만질 필요없이 저장소 구현 만 변경하면됩니다. 저장소 인터페이스가 IQueryable (누출 추상화라고 함)과 같은 구현 세부 정보를 제공하기 때문에 제공 한 코드 예제는 좋지 않습니다.

  2. 전체 지속성 모델을 매핑하지 않아도됩니다. 앱의 현재 요구 사항에 대해서만 매핑합니다. 응용 프로그램에서 Foo 객체를 원하면 Ef (여러 테이블과 복잡한 쿼리를 포함 할 수 있음)로 필요한 모든 Foo 데이터를 가져 와서 Foo에 매핑합니다. Bar 객체가 매우 간단하고 EF 엔티티와 거의 1 대 1이면 엔티티를 객체에 복사하면됩니다 (나중에 EF에서 다른 orm으로 쉽게 전환 할 수 있습니다)

  3. 그렇지 않습니다. 이것들은 뷰 모델 클래스가 아닌 퍼시스턴스 클래스입니다. 대부분의 EF 튜토리얼은 매우 열악한 직업이므로 EF 엔티티를 어디서나 모델로 직접 사용한다고 생각하게 만듭니다. 보기 모델은보기 요구를 충족시키는 별도의 모델입니다. 퍼시스턴스 모델을 통해 생성됩니다. 저장소 (쿼리에 대해 다른 전문화 된 repo)는 쿼리 결과로 생성 된 EF 엔티티를 직접 View Model의 비트로 매핑합니다. 다시 한번, 다른 Orm으로 전환하면 뷰 모델에 대해 아무 것도 변경할 필요가 없습니다.

위와 같이 리포지토리가 제대로 작동하고 올바르게 계층화 된 응용 프로그램을 유지 관리해야하는 중요한 책임이 있습니다. 하지만 EF는 저장소의 구현 세부 사항이며 앱 또는 뷰의 모델을 디자인 할 때 EF 또는 db 구조를 잊어 버리고 저장소 요구 사항에 따라 설계된 저장소 인터페이스에 대해서만 알고 있어야합니다.

+0

매우 명확한 마이크 감사합니다. –

+0

이렇게 생각한 후에 저장소 모델에서 내 비즈니스 모델 사이의 변환을 수행해야한다면 일반 저장소를 계속 사용할 수 있습니까?별로 옳지 않은가? 왜냐하면 나는 각 객체의 특정 속성에 대해 알아야 할 필요가 있기 때문입니다 ... 또한 여러분은 NHibernate 또는 다른 ORM 기술을 사용할 수 있도록 Entity 프레임 워크를 추상화하는 방법을 확실히 모릅니다. –

+1

일반 저장소는 http://bit.ly/IcYRcG의 안티 패턴으로 간주됩니다. 전환에 관해서 나는 방금 그 질문을 물었다. (http://stackoverflow.com/questions/10040950/optimum-way-to-restore-domain-object와 나는 그것에 대해 막 막 냈다. http://bit.ly/ I2fTL1). EF를 추상화하는 것에 대해, 당신은 EF 자체를 추상화하지 않으며, repo는 자신이 사용하고있는 사실을 추상화합니다. 코드를 NH로 전환 할 수는 있지만 모든 EF 관련 코드를 NH 개념과 코드로 변환해야한다는 의미입니다. 그러나 저장소는 여전히 이전과 동일한 객체를 반환합니다. 앱의 나머지 부분은 변경되지 않았습니다. – MikeSW