2012-04-09 4 views
2

MVC3을 실행중인 프로젝트가 있습니다. LINQ를 사용하여 데이터베이스에서 데이터를 가져옵니다. MVC3과 함께 제공되는 예전의 예제와 동일한 아키텍처 디자인으로 프로젝트를 만들었습니다. 이러한 프로젝트에서는 응용 프로그램이 분리되어 있으며이 항목에서는 Model.cs 파일에 중점을두고 자합니다. 지금은 각 컨트롤러마다 하나씩 가지고 있습니다. 예를 들어, HighscoreController.cs와 HighscoreModels.cs가 있습니다. 모델 클래스에서 나는 datacontext에 대한 참조를 가진 Service 클래스와이 datacontext를 사용하여 데이터베이스를 쿼리하는 몇 가지 메소드를 정의한다. 이제는 이러한 메서드 중 일부가 동일한 쿼리를 실행한다는 문제가 발생하여 데이터베이스에 대한 중앙 액세스 지점을 만들어서 리포지토리 패턴을 구현할 것이라고 생각했습니다.MVC, 저장소 패턴 및 DataLoadOptions

private IRepository _repository; 

    public HighscoreService() 
     : this(new Repository()) 
    { } 

    public HighscoreService(IRepository repository) 
    { 
     _repository = repository; 
    } 

지금 데이터베이스 호출이 저장소 내에서 처리하고 저장소 서비스 클래스에서 사용됩니다 그래서 대신에 서비스 클래스의 데이터 컨텍스트에 대한 참조를 갖는 지금과 같은 저장소에 대한 참조가 _repository 참조를 통해.

내 저장소는 다음과 같이 내장되어 있습니다 :이 저장소 패턴과 함께 DataLoadOptions를 사용하려고하면

public class Repository : IRepository 
    { 
    private MyDataContext _dataContext; 

    public Repository() 
    { 
     _dataContext = new MyDataContext(); 
    } 

    public Member MemberByName(string memberName) 
    { 
     Member member = CompiledQueries.MemberByName(_dataContext, memberName); 
     return member; 
    } 
    } 

내가 직면 문제가 나타납니다.

dataloadoptions를 사용할 때 새로운 dataloadoptions가 적용되기 전에 datacontext에 대한 이전 쿼리를 작성하지 않아야합니다. 그리고 내 저장소는 모든 메소드에서 datacontext를 재사용하므로 전혀 작동하지 않습니다. 필자는 2 가지를 시도해 왔습니다. 하나는 using 문을 사용하여 datacontext를 매번 새로 고치는 방법으로 모든 메소드 내에서 datacontext를 재생성하는 것입니다. 하지만 저장소에서 결과를 가져 와서 내 모델로 다시 가져 왔고 사용 문이 끝나면 저장소 패턴 내에서 범위가 부족할 때 문제가 발생합니다. 즉 결과가 예를 들어 사용할 수 없다는 것을 의미합니다. 나에게 데이터를 제공 한 datacontext가 종료 되었기 때문에 .Count() 또는 .ToList(). 또한 전체 저장소에서 동일한 datacontext를 사용하는 다른 솔루션을 시도했지만 dataloadoptions를 사용하는 각 메서드에서 새 인스턴스를 만듭니다. 이 느낌이 매우 더러운;) 그래서 누구나 저에게 저장소 패턴과 함께 DataLoadOptions를 사용하는 방법에 대한 제안을 줄 수 있습니까? 방금 설명한 문제를 피하십시오. 아니면 dataloadoptions를 사용하지 말고 다른 방법을 선택해야합니까? DataLoadOptions를 사용하는 이유는 관련 테이블에서 일부 데이터를 가져 오려고하기 때문입니다.

약간의 질문으로 : 위의 코드 예제에서 CompiledQueries를 자체의 .cs 파일 내에 배치 한 것을 볼 수 있습니다. 이것은 나쁜 디자인입니까? MVC 응용 프로그램에 컴파일 된 쿼리를 넣을 위치에 대한 지침이 있습니까?

미리 양해 해 주셔서 감사합니다. 내 질문에 대한 답변이 몇 개 있기를 바랍니다.) 미리 감사드립니다. 더 많은 정보가 필요하면 그냥 물어보십시오.

+0

저는 전문가는 아니지만 "요청 범위"에 데이터 컨텍스트를 유지하는 아이디어가 도움이 될 것이라고 생각합니다. 이렇게하면 HTTP 요청마다 새로운 컨텍스트가 만들어져 일부 문제가 제거 될 수 있습니다. Ninject와 같은 IoC 컨테이너가이를 지원할 수 있습니다. Entity Framework를 참조하는 동안 다음 게시물이 도움이 될 수 있습니다. http://buildstarted.com/2010/08/24/dependency-injection-with-ninject-moq-and-unit-testing/ – ngm

+0

저장소 및 설정을 주입하십시오. perWebRequest와 같은 라이프 스타일. 쓸모없는 추상화 레이어를 추가하는 것을 제외하고 저장소가 여러분을 위해 무엇을하고 있는지 확실하지 않습니다. – CrazyCoderz

답변

0

나는 전문가 인 DataLoadOptions을 결코 의미하지는 않지만 귀하의 질문과 내가 읽은 내용은 열심히로드하는 데 사용해야한다는 것입니다.

"dataloadoptions를 사용하면 새로운 dataloadoptions가 적용되기 전에 datacontext에 대한 이전 쿼리를 작성하지 않아야합니다."

.. 내게 이것은 DataLoadOptions (나는 개인적으로 LINQ to SQL이 아닌 Entity Framework를 사용함)이라는 결점이나 디자인 결함과 같은 것 같습니다.ngm과 CrazyCoderz가 처음 2 개의 코멘트에서 제안한대로 HTTP 요청 당 하나의 데이터 컨텍스트를 갖는 것이 좋을 것이라고 생각하지만 이것이 문제를 해결할 것이라고는 생각하지 않습니다. 단일 HTTP 요청 내에서 단일 데이터 컨텍스트를 다시 사용하려면 첫 번째 쿼리를 실행하자마자 데이터 컨텍스트에서 DataLoadOptions을 새 값으로 설정할 수없는 것처럼 들립니다.

발표자가 언급 한 솔루션 중 하나를 제공하는 vslive vegas에서 프리젠 테이션을보고 각 리포지토리 방법에 새로운 데이터 컨텍스트를 만들었습니다. 여기서해야 할 일은 using 문을 종료하고 메서드 결과를 반환하기 전에 ToList() 또는 ToArray()를 으로 호출 한 다음을 호출하는 것입니다.

언급 한대로 메서드를 반환하면 열거 형에 미리로드되지 않은 개체가 액세스 할 수 없게됩니다. 당신이 이미 가지고있는 경우, 쿼리를 실행하고 List, Collection, Array, 또는 다른 콘크리트 IEnumerable로 변환, 당신은 더 이상 Count() 또는 ToList()방법에 액세스 할 필요가 없습니다. 대신 Array.Length 또는 List.Count 또는 Collection.Count속성을 사용할 수 있습니다.

다른 모든 방법으로 각 저장소 방법에서 새 데이터 컨텍스트를 만들지 못하게합니까? 의미, 당신은 처분 되었기 때문에 얻을 수없는 저장소 방법을 실행 한 후, 데이터 컨텍스트가 무엇이 필요합니까? 첫 번째 쿼리 의견

회신이 작업을 수행 할 수 있습니까?

public Member GetSomeRandomMember() 
{ 
    Member[] members = null; 
    using (var context = new MyDataContext()) 
    { 
     // execute the query to get the whole table 
     members = context.Members.ToArray(); 
    } 

    // do not need to query again 
    var totalRows = members.Length; 
    var skipThisMany = PerformRandomNumberComputation(totalRows); 
    return members.Skip(skipThisMany).FirstOrDefault(); 
} 

회원 테이블에 행이 많은 경우 적합하지 않을 수 있습니다. 이 경우 2 개의 쿼리를 실행하려면 1을 계산하고 2 번째 쿼리를 실행하면됩니다. 당신은이 상황을 열어 그것을 달성 할 수 :

의견의 두 번째 부분에 대한
public Member GetSomeRandomMember() 
{ 
    using (var context1 = new MyDataContext()) 
     var totalRows = context1.Members.Count(); 

    var skipThisMany = PerformRandomNumberComputation(totalRows); 

    Member member = null; 
    using (var context2 = new MyDataContext()) 
     member = context2.Members.Skip(skipThisMany).FirstOrDefault(); 

    return member; 
} 

, 난 당신이 무엇에 관해 얘기하는지 내가 얻을 모르겠어요. 리포지토리 방법에 전체 개체를 전달하려는 경우

public void SaveMember(int id, string email, bool isSuspended) 
{ 
    using (var context = new MyDataContext()) 
    { 
     var member = context.Members.Single(m => m.Id == id); 
     member.Email = email; 
     member.IsSuspended = isSuspended; 
     context.SaveChanges(); // or whatever the linq to sql equivalent is 
    } 
} 

, 당신은 아직도 그것을 쿼리해야 데이터와 그에 대한 결정 변경의 페치 모든 어쨌든 하나의 맥락으로 단일 작업으로 와야한다 올바른 컨텍스트에 첨부됩니다.

public void SaveMember(Member member) 
{ 
    var memberDto = member; 
    using (var context = new MyDataContext()) 
    { 
     member = context.Members.Single(m => m.Id == memberDto.Id); 
     member.Email = memberDto.Email; 
     member.IsSuspended = memberDto.IsSuspended; 
     context.SaveChanges(); // or whatever the linq to sql equivalent is 
    } 
} 
+0

답변 해 주셔서 감사합니다. 이제 전체 테이블을로드하고, 그 다음에는 카운트를 수행하고, 카운트에 기반하여 난수를 생성합니다.이 난수는 .Skip (randomNumber) .FirstOrDefault()에서 동일한로드 된 결과에 사용됩니다. 그래서 내가 느슨하게로드 할 수 없다면 2 개의 쿼리로 나누어야합니다. 하나는 카운트를, 하나는 실제 행을 가져 오는 것입니다.컨텍스트를 종료 할 때 또 다른 문제점은 컨텍스트가 종료 될 때 이전에 가져온로드 된 결과에 변경 내용을 제대로 삽입 할 수 없으므로 데이터를 가져 와서 변경하고 다시 넣는 것입니다. – emin

+0

답안과 훌륭한 코드 예제에 대해 다시 한번 감사드립니다. 성능이 무겁다는 것을 알지 못하기 때문에 많은 datacontext를 열어 첫 번째 부분에 대해 어떻게 생각하는지 모르겠습니다. 나는 조금 더 깨끗한 마법의 해결책을 원하고 있었다고 생각한다;) 두 번째 부분은 많은 의미를 가지며, 나는 그것을 대신 할 수 있도록 확실히 할 것이다. 감사합니다. – emin

+0

@emin, 두 개의 데이터 컨텍스트를 열어서 처리하는 것이 이상적이라고는 생각하지 않지만, 이것은 한 옵션의 성능과 다른 옵션의 성능을 비교해야 할 수도 있습니다. 첫 번째 코드 스 니펫을 수행하면 본질적으로 db에서 SELECT * FROM Members를 실행합니다. 이렇게하면 db가 모든 행을 메모리로 반환합니다. 회원 테이블에 수천 또는 수백만 개의 행이 있다면 이는 느리고 비용이 많이 듭니다. 두 번째 옵션은'SELECT COUNT (*) FROM Members'를 선택하고'SELECT * FROM Members WHERE ID = @ ID'를 대신하며, 테이블에 많은 행이있을 때 항상 더 성능이 좋습니다. – danludwig