2012-03-09 2 views
3

다음과 같은 개체 구조가 있습니다.재귀 linq 쿼리

public class Study 
{ 
    public Guid? PreviousStudyVersionId { get; set; } 
    public Guid StudyId { get; set; } 
    //Other members left for brevity 
} 

엔티티 프레임 워크 코드를 먼저 사용하여 유지됩니다.

그것은 이제 모든 studyId의 재귀를 조회 할이

PreviousStudyVersionId     StudyId 
EF90F9DC-C588-4136-8AAE-A00E010CE87B E4315CFD-9638-4225-998E-A00E010CEEEC 
NULL         1C965285-788A-4B67-9894-3D0D46949F11 
1C965285-788A-4B67-9894-3D0D46949F11 7095B746-8D32-4CC5-80A9-A00E010CE0EA 
7095B746-8D32-4CC5-80A9-A00E010CE0EA EF90F9DC-C588-4136-8AAE-A00E010CE87B 

같은 테이블에 발생합니다. 그래서 다음 솔루션을 생각해 냈습니다.

그래서 내 저장소 GetAllStudyVersionIds (new Guid ("7095B746-8D32-4CC5-80A9-A00E010CE0EA"))의 메소드를 호출하면 studyId가 모두 4 개가 반환됩니다.

public IEnumerable<Guid> GetAllStudyVersionIds(Guid studyId) 
    { 
     return SearchPairsForward(studyId).Select(s => s.Item1) 
      .Union(SearchPairsBackward(studyId).Select(s => s.Item1)).Distinct(); 
    } 

    private IEnumerable<Tuple<Guid, Guid?>> SearchPairsForward(Guid studyId) 
    { 
     var result = 
      GetAll().Where(s => s.PreviousStudyVersionId == studyId).ToList() 
      .Select(s => new Tuple<Guid, Guid?>(s.StudyId, s.PreviousStudyVersionId)); 
     result = result.Traverse(a => SearchPairsForward(a.Item1)); 
     return result; 
    } 

    private IEnumerable<Tuple<Guid, Guid?>> SearchPairsBackward(Guid studyId) 
    { 
     var result = GetAll().Where(s => s.StudyId == studyId).ToList() 
      .Select(s => new Tuple<Guid, Guid?>(s.StudyId, s.PreviousStudyVersionId)); 
     result = result.Traverse(a => a.Item2.HasValue ? SearchPairsBackward(a.Item2.Value) : Enumerable.Empty<Tuple<Guid, Guid?>>()); 
     return result; 
    } 

이것은 내 확장 메소드의 구현입니다.

public static class MyExtensions 
{ 
    public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse) 
    { 
     foreach (var item in source) 
     { 
      yield return item; 
      var seqRecurse = fnRecurse(item); 
      if (seqRecurse == null) continue; 
      foreach (var itemRecurse in Traverse(seqRecurse, fnRecurse)) 
      { 
       yield return itemRecurse; 
      } 
     } 
    } 
} 

이 코드를 데이터베이스 (IQueryable)에 가깝게 이동하고이 코드를 최적화 할 수있는 방법이 있습니까?

+0

결국 SQL 쿼리를 작성하고 싶을 수도 있습니다. 다른 데이터베이스 플랫폼이이를 다르게 수행 할 수 있습니다. MS SQL Server는 [재귀 CTE] (http://msdn.microsoft.com/en-us/library/ms186243.aspx)를 사용하여이를 수행 할 수 있습니다. – vcsjones

+0

열의 이름을 추측하여 효과적으로 연결되는 이력 항목 목록을 만듭니다. 예를 들어 관계형 모델과 잘 어울리는 다르게 저장하는 방법이있을 수 있습니다. 학습 테이블과 그것을 참조하는 역사적인 아이템으로 가득 찬 테이블을 가지고 있습니다. – OlduwanSteve

답변

1

나는이 작업을 id을 기반으로 모든 부모 레코드를 가져온 다음 재귀 테이블 함수를 만들어 각 레코드와 모든 부모에 대한 목록을 생성하는보기를 만들었습니다. 그런 다음 EF 모델에서이를 소비하고 LINQ와 함께 사용할 수 있도록 연결합니다.