2009-10-21 2 views
1

SubSonic 3.0.0.3 ActiveRecord를 사용하여 소규모 프로젝트를 구축하고 있으며 과거에 얻을 수없는 문제가 발생했습니다.LINQ 및 SubSonic을 사용한 개체 매핑

var result = from r in Release.All() 
      let i = Install.All().Count(x => x.ReleaseId == r.Id) 
      where r.ProductId == productId 
      select new ReleaseInfo 
      { 
       NumberOfInstalls = i, 
       Release = new Release 
       { 
        Id = r.Id, 
        ProductId = r.ProductId, 
        ReleaseNumber = r.ReleaseNumber, 
        RevisionNumber = r.RevisionNumber, 
        ReleaseDate = r.ReleaseDate, 
        ReleasedBy = r.ReleasedBy 
       } 
      }; 

ReleaseInfo 객체가 사용자 정의 클래스이며, 다음과 같습니다 :

public class ReleaseInfo 
{ 
    public Release Release { get; set; } 
    public int NumberOfInstalls { get; set; } 
} 

해제 및 음속에 의해 생성 된 클래스를하는 설치

다음은 LINQ 쿼리입니다.

결과에서 감시 할 때 Release 속성은 null입니다.

이 방법을 좀 더 간단한 쿼리로 만들고 결과를 보면 값이 null이 아닙니다. 이 내 LINQ 쿼리 또는 음속의 제한에 문제가

var result = from r in Release.All() 
      let i = Install.All().Count(x => x.ReleaseId == r.Id) 
      where r.ProductId == productId 
      select new Release 
      { 
       Id = r.Id, 
       ProductId = r.ProductId, 
       ReleaseNumber = r.ReleaseNumber, 
       RevisionNumber = r.RevisionNumber, 
       ReleaseDate = r.ReleaseDate, 
       ReleasedBy = r.ReleasedBy 
      }; 

인가?

+0

ReleaseInfo 객체를 만들기 전에 간단한 변수를 사용하여 (let을 사용하여) 임시 변수를 만들려고 했습니까? –

+0

let 부분을 쿼리에서 제거하더라도 동일한 문제가 있습니다. –

답변

1

이 문제에 대한 실제 답변을 찾은 것 같습니다. 나는 SubSonic 소스를 뒤적 거리며 데이터 아더를 객체에 매핑 할 때 사용되는 두 가지 유형의 객체 투영법을 발견했습니다. 하나는 익명 유형 및 그룹화이고 다른 하나는 다른 모든 것입니다.

다음은 스 니펫입니다 : 라인 269 - SubSonic.Linq.Structure.DbQueryProvider

IEnumerable<T> result; 
Type type = typeof (T); 
//this is so hacky - the issue is that the Projector below uses Expression.Convert, which is a bottleneck 
//it's about 10x slower than our ToEnumerable. Our ToEnumerable, however, stumbles on Anon types and groupings 
//since it doesn't know how to instantiate them (I tried - not smart enough). So we do some trickery here. 
    if (type.Name.Contains("AnonymousType") || type.Name.StartsWith("Grouping`") || type.FullName.StartsWith("System.")) { 
    var reader = _provider.ExecuteReader(cmd); 
    result = Project(reader, query.Projector); 
    } else 
    { 
     using (var reader = _provider.ExecuteReader(cmd)) 
     { 
      //use our reader stuff 
      //thanks to Pascal LaCroix for the help here... 
      var resultType = typeof (T); 
      if (resultType.IsValueType) 
      { 
       result = reader.ToEnumerableValueType<T>(); 
      } 
      else 
      { 
       result = reader.ToEnumerable<T>(); 
      } 
     } 
    } 
    return result; 

298은 음속 ToEnumerable는 당신이 투사하려는 객체의 속성에 DataReader가에 열 이름을 일치 시키려고하는 것이 밝혀졌습니다.내 Linq에에서 SQL 쿼리는 다음과 같습니다.

SELECT [t0].[Id], [t0].[ProductId], [t0].[ReleaseDate], [t0].[ReleasedBy], [t0].[ReleaseNumber], [t0].[RevisionNumber], [t0].[c0] 
FROM (
    SELECT [t1].[Id], [t1].[ProductId], [t1].[ReleaseDate], [t1].[ReleasedBy], [t1].[ReleaseNumber], [t1].[RevisionNumber], (
    SELECT COUNT(*) 
    FROM [dbo].[Install] AS t2 
    WHERE ([t2].[ReleaseId] = [t1].[Id]) 
    ) AS c0 
    FROM [dbo].[Release] AS t1 
) AS t0 
WHERE ([t0].[ProductId] = 2) 

주목하라 [T0] [C0] 내 재산 이름 NumberOfInstalls과 동일하지 않습니다. 따라서 c0의 값은 결코 내 객체에 투영되지 않습니다.

수정 사항 : if 문을 꺼내고 10 배 느린 투영을 사용하면 모든 것이 작동합니다.

1

본인은 본질적으로 ORM의 기능을 복제하고있는 것일 수 있습니다.

from r in Release.All() 

이 라인은 데이터베이스의 모든 항목에 대한 완전히 채워 릴리스 레코드 목록을 반환 이해하는 중요한 점은이 라인입니다. SubSonic이 이미 당신을 위해 채워 넣은 것들만 반환하면됩니다.

이 논리를 사용하여 다음을 수행 할 수 있어야한다 : 즉 대단히 비효율적 될 가능성이 있기 때문에

말했다되고 그건
var result = from r in Release.All() 
       select new ReleaseInfo { 
        Release = r, 
        NumberOfInstalls = Install.All().Count(x => x.ReleaseId == r.Id) 
       }; 

, 당신의 Install.All() 호출에 보일 것입니다. 데이터베이스에서 모든 설치를 가져 와서 해당 설치를 개체에 보급 한 다음 .NET의 모든 레코드 ID를 비교하여 레코드가 해당 조건을 충족하는지 확인합니다. SubSonic의 .Find 메서드를 사용하여 데이터베이스 계층의 특정 레코드 만 반환하면 성능이 크게 향상됩니다. 여전히, 물체를 팽창시키는 것은 여전히 ​​비쌀 수 있으며 뷰 또는 저장 프로 시저를 여기에서 고려할 수 있습니다. 그러나 간단한 첫 단계로, 다음과 같은 작업을해야합니다 :

var result = from r in Release.All() 
      select new ReleaseInfo { 
       Release = r, 
       NumberOfInstalls = Install.Find(x => x.ReleaseId == r.Id).Count() 
      }; 
+0

Release.All()은 IQueryable을 반환합니다 ... 열거되기 전까지 데이터베이스에 절대 도달하지 않습니다. 문제는 쿼리가 아니라 SubSonic이 쿼리 결과를 개체에 매핑하는 방식입니다. 해결책은 http://stackoverflow.com/questions/1655354/subsonic-3-0-and-linq/1670198#1670198을 참조하십시오. –

0

우리는 특정 occassions에에 여행 돌기 버그가 - 나는 그것이 패치가 생각하지만 난 더 테스트해야합니다. 나는 당신에게 최신 비트를 시험해 보라고 초대합니다 - 우리는 그것을 수정했다고 생각합니다 ... 너무 애매한 것은 유감 스럽지만 버그는 3.0.0.1과 3.0.0.3 사이에 있었고 그것을 찾을 수 없었습니다.

+0

Rob, 3.0.0.3을 사용 중입니다. 당신이 관심이 있다면, 나는 대답으로 표시된 게시물이 버그가있는 곳을 정확하게 보여줄 것이라고 생각합니다. 이것을 볼 시간을내어 주셔서 감사합니다. –

-1

3.0.0.4에서 수정 되었습니까? 나는이 게시물을 찾기 위해 너무 오줌 누웠다. 내 투영이 작동하지 않는 이유를 알아 내려고 2 일 후에 - 속성 이름이 쿼리와 정확하게 일치하는 경우를 제외하고 - 나는 여기서 끝났습니다. SS SimpleRepository에 너무 의존적이어서 지금은 되돌릴 수 없습니다. 이런 버그는 절뚝 거리고 있습니다. 어떤 경우에 그것은 분류됩니까?

지금은 10 배 느린 경로로 갔으므로 적어도 내 고객에게 공개 할 수 있습니다. 많이 작동하는 빠른 방법을 선호합니다 :)

+0

그것은 3.0.0.4 릴리스 발표에서 문제가 해결되었다고 말하지만, 현재 빌드와이 최신 GitHub 소스에서이 문제가 여전히 발생하고 있습니다. –