2011-10-16 3 views
2

Type member support in LINQ-to-Entities?에서 LINQ에서 쿼리 할 클래스 속성을 선언하려고 시도했지만 문제가 발생했습니다. 여기서는 구현 내부에서 코드를 쿼리로 변환하기위한 도움을 얻기 위해 코드를 배치합니다.루프를 단일 linq 표현식으로 병합하기

는 I가 QuestionLevel에 따라 분류되고, 각각의 Question (S)의 컬렉션을 포함하는 클래스 Quiz가 ... I 퀴즈가 통해 수행되는 "개방"또는 "폐쇄"인지 여부를 결정해야 최대 값 표와 비교하여 질문 수준 및 각 수준의 질문 수에 대한 외부 조인 그래서 여기

public partial class Quiz 
{ 
    public bool IsClosed 
    { 
     get 
     { 
      // if quiz has no questions, it's open 
      if (this.Questions.Count() == 0) return false; 

      // get a new handle to the EF container to do a query for max values 
      using (EFContainer db = new EFContainer()) 
      { 
       // we get a dictionary of LevelName/number 
       Dictionary<string, int> max = db.Registry 
        .Where(x => x.Domain == "Quiz") 
        .ToDictionary(x => x.Key, x => Convert.ToInt32(x.Value)); 
       // count the number of questions in each level, comparing to the maxima 
       // if any of them are less, the quiz is "open" 
       foreach (QuestionLevel ql in db.QuestionLevels) 
       { 
        if (this.Questions.Where(x => x.Level == ql).Count() < max["Q:Max:" + ql.Name]) 
         return false; 
       } 
      } 
      // the quiz is closed 
      return true; 
     } 
    } 
} 

그것을 나의하지 아직 작업을 시도입니다 : 여기에 그대로 코드의

유형 : 그것은 불평, 조인에 계정에 실패

public static IQueryable<Quiz> WhereIsOpen(this IQueryable<Quiz> query) 
    { 
     EFContainer db = new EFContainer(); 
     return from ql in db.QuestionLevels 
       join q in query on ql equals q.Questions.Select(x => x.Level) 
       into qs 
       from q in qs.DefaultIfEmpty() 
       where q.Questions.Count() < db.Registry 
        .Where(x => x.Domain == "Quiz") 
        .Where(x => x.Key == "Q:Max" + ql.Name) 
        .Select(x => Convert.ToInt32(x.Value)) 
       select q; 
    } 

조인 절에있는 식 중 하나가 잘못되었습니다. 'GroupJoin'에 대한 호출에서 타입 추론에 실패했습니다.

저는 이것을 알아 내려고하고 있습니다.

* 업데이트 나는

아 *. 바보 나.

join q in query on ql equals q.Questions.Select(x => x.Level).Single() 

또 하나의 장애물 :

지정된 LINQ 표현이 다른 문맥과 관련된 있는 쿼리에 대한 참조를 포함합니다.

이것은 최대 조회를 위해 생성 한 새 컨테이너 때문입니다. 그래서 나는 다시 요소를이 같은 생각 :

public static IQueryable<Quiz> WhereIsOpen(this IQueryable<Quiz> query) 
    { 
     EFContainer db = new EFContainer(); 
     IEnumerable<QuestionLevel> QuestionLevels = db.QuestionLevels.ToList(); 
     Dictionary<string, int> max = db.Registry 
       .Where(x => x.Domain == "Quiz") 
       .ToDictionary(x => x.Key, x => Convert.ToInt32(x.Value)); 
     return from ql in QuestionLevels 
       join q in query on ql equals q.Questions.Select(x => x.Level).Single() 
       into qs 
       from q in qs.DefaultIfEmpty() 
       where q.Questions.Count() < max["Q:Max:" + ql.Name] 
       select q; 
    } 

하지만 런타임 예외를 생산, 나는 그것이 된 IQueryable에 QuestionLevels을 캐스팅 날 필요로 ... 컴파일 할 수있는 표현을 얻을 수 없다 (하지만 캐스팅이 작동하지 않습니다). *

* 업데이트 II는 내가 캐스팅 문제에 대한 해결책을 찾았지만 지금은 다시 "다른 컨텍스트"예외입니다. grr ...

return from ql in QuestionLevels.AsQueryable() 

* 갱신 (커크의 제안) *이

그래서 내가 지금 컴파일하지만 런타임 예외를 생성하는이 있습니다

public static IQueryable<Quiz> WhereIsOpen(this IQueryable<Quiz> query) 
{ 
    EFContainer db = new EFContainer(); 
    IEnumerable<string> QuestionLevels = db.QuestionLevels.Select(x => x.Name).ToList(); 
    Dictionary<string, int> max = db.Registry 
      .Where(x => x.Domain == "Quiz") 
      .ToDictionary(x => x.Key, x => Convert.ToInt32(x.Value)); 
    return from ql in QuestionLevels.AsQueryable() 
      join q in query on ql equals q.Questions.Select(x => x.Level.Name).Single() 
      into qs 
      from q in qs.DefaultIfEmpty() 
      where q.Questions.Count() < max["Q:Max:" + ql] 
      select q; 
} 

나는 다음과 같이 전화 :

List<Product> p = db.Quizes.WhereIsOpen().Select(x => x.Component.Product).ToList(); 

결과 예외 :

이 방법은 엔티티 인프라에 LINQ를 지원하며 사용자 코드에서 직접 사용할 수 없습니다 된다.

+0

그래서 정확히 무엇이 문제이며, 무엇을하려고합니까? 현재 코드가 작동합니까? 이 질문을 정돈하여 정확하게 달성하려는 내용과 문제가 무엇인지 명확히 설명 할 수 있습니다. –

+0

@KirkBroadhurst, 내가 가진 문제는 내 확장의 중간에 조회를해야하며 두 가지 상황에 합류 할 수없는 것입니다. – ekkis

+0

@KirkBroadhurst, 조금이라도 반가워하면 죄송합니다. 그것은 내가 타격을 가하는 많은 장애물을 보여주기위한 것입니다. 그리고 아니오,이 시점에서 코드가 작동하지 않습니다. QuestionLevels에 가입하면 두 개의 컨텍스트가 있다고 생각하게됩니다.하지만 QuestionLevel에 메모리 내 개체가 있어야하기 때문에 실제로는 없어야합니다 ... 이해가 안됩니다. – ekkis

답변

5

데이터베이스 개체를 도메인 개체에 연결할 때 일반적으로 발생하는 문제가 있습니다. 도메인을 나타내는 별도의 클래스 세트와 데이터베이스를 대표하고 데이터베이스 CRUD에 사용되는 별도의 클래스 세트를 갖는 것이 좋은 이유입니다. 속성의 겹침이 예상되지만이 방법은 응용 프로그램을보다 잘 제어하고 비즈니스 논리에서 데이터베이스를 분리합니다.

퀴즈가 닫힌다는 생각은 도메인 (비즈니스 로직)에 속합니다. 퀴즈를 반환 할 때 DAL (데이터 액세스 계층)이 모든 필요한 테이블을 조인해야하므로 닫힌 상태인지 여부를 결정하는 데 필요한 모든 정보를 사용할 수 있어야합니다. 그런 다음 도메인/서비스/비즈니스 계층에서 IsClosed 속성을 적절히 채운 도메인 객체를 만들어 UI 레이어 (MVC)에서 쉽게 액세스 할 수 있도록해야합니다.

데이터베이스 컨텍스트에 직접 액세스하는 것을 볼 수 있습니다. 나는 그것에 대해 경고하고 DI/IoC 프레임 워크 (Ninject는 훌륭합니다)를 사용하도록주의해야하지만 데이터베이스 컨텍스트에 액세스 할 것입니다. 직접 또한

사용하여 뷰/컨트롤러에서이 클래스 :

public class QuizDomainObject 
{ 
    public int Id {get; set;} 
    public bool IsClosed {get; set;} 
    // all other properties 
} 

컨트롤러 :

public class QuizController : Controller 
{ 
    public ActionResult View(int id) 
    { 
     // using a DI/IoC container is the 
     // preferred method instead of 
     // manually creating a service 
     var quizService = new QuizService(); 
     QuizDomainObject quiz = quizService.GetQuiz(id); 

     return View(quiz); 
    } 
} 

서비스/비즈니스 레이어 :

public class QuizService 
{ 
    public QuizDomainObject GetQuiz(int id) 
    { 
     // using a DI/IoC container is the 
     // preferred method instead of 
     // access the datacontext directly 
     using (EFContainer db = new EFContainer()) 
     { 
      Dictionary<string, int> max = db.Registry 
       .Where(x => x.Domain == "Quiz") 
       .ToDictionary(x => x.Key, x => Convert.ToInt32(x.Value)); 

      var quiz = from q in db.Quizes 
         where q.Id equals id 
         select new QuizDomainObject() 
         { 
          Id = q.Id, 
          // all other propeties, 

          // I'm still unclear about the structure of your 
          // database and how it interlates, you'll need 
          // to figure out the query correctly here 
          IsClosed = from q in .... 
         }; 


      return quiz; 
     } 
    } 
} 
+0

제안 된 새로운 접근 방식에 대해 감사드립니다. 이것은 나의 첫 번째 MVC 프로젝트이며 주사 문제는 테스트 스위트를 작성하기 시작할 때 언급 한 것입니다. 가능한 한 빨리 앱을 작동시켜 테스트 (내가 알기로, 병렬로 수행해야 함)가 뒷자리를 차지하도록해야합니다. 나는 두 번째 프로젝트에서 내가 올바른 방법으로 그것을 할 것이라고 확신한다 ... – ekkis

+2

한 가지 나는 이해하지 못한다 : EF는 나의 모델링 공간이되어야하고 어떻게 백엔드 데이터베이스에 매핑 할 것인가? 매핑 메커니즘 ... 그래서 왜 두 번째 (DAL) 레이어를 갖고 싶습니까? DAL의 EF 모델이 아닙니까? – ekkis

+0

@ekkis 아주 좋은 의견입니다. 그러나 헤이, 당신은 결코 너무 많은 층을 가질 수 없다, 당신은 할 수 있냐?? –

1

재 : 귀하의 코멘트

는 IT 두 가지 상황이 있다고 생각하고있다 QuestionLevels에 가입 ...하지만 정말 때문에이 안됩니다 QuestionLevels는 메모리 내 객체를 포함해야합니다.

객체가 아닌 단순한 유형에 참여하면이 문제를 피할 수 있다고 생각합니다.다음은 당신을 위해 일 수 있습니다 (이 다음 작업을 일부 익명 형식을 구성하고 아이디에 가입하지 않는 경우)

문제입니다

return from ql in QuestionLevels     
     join q in query 
     on ql.LevelId equals q.Questions.Select(x => x.Level).Single().LevelId 
     into qs 

즉, 레벨에 객체에 합류은 EF의 원인 언더 커버 마법을하려면 데이터베이스의 오브젝트를 찾아서 거기에 참여하십시오. 당신이 간단한 타입에 합류하라고 말하면, 데이터베이스에 값을 SELECT으로 보내고, 객체를 검색하여 응용 프로그램 계층에서 다시 스티치해야합니다.

+0

나는 당신의 아이디어를 구현했다. (나는 최신 정보를 볼 수있다. 나는이 코드를 코드에 붙여 넣을 수 없으므로 멋지게 만든다.) – ekkis

+0

어떤 방법이나 라인이 예외인가? –

+0

전화를 걸면 불평을합니다 ('List p ='). 스택 추적이 도움이되는지 나는 모른다. 그것은 여기 : http://pastebin.com/V9FyZ1qf – ekkis

관련 문제