2012-04-30 2 views
4

우리는 데이터 계층에 EF 4.3을 사용하고 있으며 일반적인 저장소 패턴을 사용하고 있습니다. 백엔드는 SQL 2008 R2이고 프로젝트는 .NET 4.0/MVC 3이지만이 요소가 문제가되지는 않을 것이라고 생각합니다.EF 4.3 다중 열거 문제

기본적으로 우리는 두 개의 객체로 구성된 데이터베이스에 일대일 관계가 있습니다. 하나는 '함정'을위한 것이고 두 번째는 '함정 활동'을위한 것입니다. 즉, 이러한 '트랩'중 하나가 배포되면 해당 트랩에 발생하는 모든 내용이 트랩 활동 테이블에 보관됩니다. 이 작업을 수행하는 방법은 매우 간단해야합니다.

관계는 '트랩 활동'테이블의 '트랩'테이블의 PK에 대한 FK로 정의됩니다. 두 테이블 모두 PK가 정의되어 있습니다.

서비스 레이어에서 이러한 트랩이 배포 된 날짜가있는 '트랩'목록을 쿼리해야합니다. 이는 다음 코드 스 니펫으로 수행됩니다.

var traps = this.trapRepository.Find(x => x.DeploymentYear == 2012).Select(x => new TrapHomeViewModel 
      { 
       County = x.County.Name, 
       DeploymentDate = x.TrapActivities.First(y => y.ActivityType == 1).ActivityDate, 
       State = x.County.CountyState.Abbreviation, 
       Latitude = x.Latitude, 
       Longitude = x.Longitude, 
       TrapId = x.TrapID, 
       TrapNumber = x.SerialNumber, 
       Centroid = x.TrapCentroid 
      }).ToList(); 

문제는 DeploymentDate 속성에서 발생합니다. 서면으로, 이것은 대략 3000의 품목의 명부를 돌려 보내기 위하여 25s를 가지고 간다. 트랩 테이블을 업데이트하여 배포 날짜를 저장하고이 행을 채우십시오.

DeploymentDate = x.DeploymentDate.Value.Date 

결과가 1 초 미만입니다.

SELECT  Counties.Name, TrapActivities.ActivityDate, States.Abbreviation, 
Traps.Latitude, Traps.Longitude, Traps.TrapID, Traps.SerialNumber, Traps.TrapCentroid 
    FROM   TrapActivities INNER JOIN 
          Traps ON TrapActivities.TrapID = Traps.TrapID INNER JOIN 
          Counties ON Traps.CountyID = Counties.CountyID INNER JOIN 
          States ON Counties.State = States.FIPS_Code 
    WHERE  (TrapActivities.ActivityType = 1) 

...하지만이 될 것 같지 않습니다 : 지금은 내가 여기에 것입니다 (데이터 세트의 여러 열거)하지만 다음과 유사한 쿼리 될 것 일어날 것이라고 생각했던 것을 알고 있다고 생각 경우. 위의 모든 배경 정보와 함께,이 뷰 모델을 채우는 데 어디서 이탈 했습니까? 이전에이 문제에 부딪혔다 고 생각하지 않지만 이는 다른 프로젝트보다 훨씬 큰 데이터 세트입니다. 이것에 대한 지침은 많은 도움이 될 것입니다. 다른 정보를 제공해야하는 경우 알려 주시기 바랍니다.

EDIT

요청한 바와 같이, GenericRepository 메소드 및 생성자 찾기 : 이것은 상기 코드에 의해 생성되는 SQL의 예

public class GenericRepository<T> : IGenericRepository<T> 
     where T : class 
    { 
     private readonly IObjectSet<T> objectSet; 
     private ObjectContext context; 

     public GenericRepository() 
      : this(new APHISEntities()) 
     { 
     } 

     public GenericRepository(ObjectContext context) 
     { 
      this.context = context; 
      this.objectSet = this.context.CreateObjectSet<T>(); 
     }  

     public IEnumerable<T> Find(Func<T, bool> predicate) 
     { 
      return this.objectSet.Where(predicate); 
     } 

EDIT 2

을 :

exec sp_executesql N'SELECT 
[Extent1].[TrapActivityID] AS [TrapActivityID], 
[Extent1].[TrapID] AS [TrapID], 
[Extent1].[ActivityType] AS [ActivityType], 
[Extent1].[Notes] AS [Notes], 
[Extent1].[AgentID] AS [AgentID], 
[Extent1].[ActivityDate] AS [ActivityDate], 
[Extent1].[CreatedOn] AS [CreatedOn], 
[Extent1].[EditedOn] AS [EditedOn], 
[Extent1].[Deleted] AS [Deleted], 
[Extent1].[VisualInspectionID] AS [VisualInspectionID] 
FROM [dbo].[TrapActivities] AS [Extent1] 
WHERE [Extent1].[TrapID] = @EntityKeyValue1',N'@EntityKeyValue1 uniqueidentifier',@EntityKeyValue1='FEBC7ED4-E726-4F5E-B2BA-FFD53AB7DF34' 

트랩 ID 목록을 가져 와서 각각에 대해 쿼리를 실행하면 수천 개의 SQL 문이 생성됩니다. 또한 카운티 정보에 대한 개별 쿼리를 실행하는 것처럼 보입니다.

+0

이 상황에서 최선의 선택이되며 미래의 상황에서는 데이터베이스에서 Anjlab SQL 프로파일 러와 같은 프로파일 러를 실행하는 것이 좋습니다. 그것은 실행중인 정확한 SQL과 각 호출에 걸리는 시간을 보여줍니다. 그런 다음 비효율적으로 매우 뛰어난 작업을 수행하는지 확인할 수 있습니다. 속도를 높이려면 데이터베이스에 인덱스를 추가하기 만하면됩니다. 25 초가 걸리는 쿼리를 찾으면 SSMS에 실행 경로를 볼 수있는 옵션이 있으며 문제가있는 위치를 확인해야합니다. – NibblyPig

+0

네트워크 트래픽 (Wire Shark가 수행 할 것입니다)을 잡아서 어떤 쿼리가 생성되는지 정확히 볼 수 있습니다. 그런 다음이를 살펴보고 인덱스를 최적화 할 수 있는지 확인하십시오. 생성하는 쿼리에 놀랄 수도 있습니다. 저는 제 일부와 함께했습니다. – itsmatt

+0

저장소의 '찾기'메소드는 어떻게 생겼습니까? –

답변

1

리포지토리에서 ObjectQuery.ToTraceString을 사용하면 개체를 반환하기 전에 어떤 SQL이 실행되는지 확인할 수 있습니다.

2012 년에 배포 된 모든 실제 Trap 개체는 IQueryable 대신 저장소 찾기 메서드에서 반환되며 사용자는 TrapActivities을 열망하지 않습니다. 즉, Select의 결과를 통해 열거하면서 뷰 모델을 만들면 각 Trap에 대한 새 쿼리를 DB에 전송하여 TrapActivities이됩니다.

업데이트 1

난 당신이에 대한 저장소에서 특정 쿼리를 구현해야합니다 생각합니다. 당신이에 얘기하지 않는 한 EF하지 열망 부하 자식 관계를 않기 때문에

var q = from t in traps 
     where t.DeploymentYear == 2012 
     select new TrapFirstDeployment { 
      Trap = t, 
      DeploymentActivity = t.TrapActivities.Where(ta=>ta.FirstOrDefault(a=>a.ActivityType=1)) 
     }; 

return q.Where(tfd=>tfd.DeploymentActivity != null); 

설명

초기 쿼리가 느렸다 이유입니다. 지연로드는 기본적으로 설정됩니다. 저장소에 Trap과 함께 TrapActivities을로드하라는 메시지를 표시하지 않으므로 처음으로 액세스 할 때까지 기다립니다. 이것은 트랩을 필요로하지만 DB가 아닌 트래픽을 감소시키기 때문에 활동이 아니라는 점에서 좋습니다. 그러나 어떤 경우에는 필요합니다. 이 경우 쿼리에 Include을 추가하여 열정적 인로드를 강제로 수행 할 수 있습니다. 예 :

var q = from t in this.objectSet.Include('TrapActivities') 
     select t; 

이렇게하면 TrapActivities의 모든 항목이 하나의 쿼리로로드됩니다. 그러나 귀하의 경우에는 TrapFirstDeployment 클래스를 만든 첫 번째 배포 작업 만 필요합니다. 이렇게하면 EF는 첫 번째 배포 작업 만 가져야합니다.

당신은 또한 IQueryable.Where 서명과 일치하는 Expression<Func<T,Boolean>>에 저장소에 Find 방법에 대한 매개 변수를 변경해야합니다 2

업데이트. IEnumerable.WhereFunc<T,Boolean>을 사용하므로 objectSetWhere이 호출되기 전에 IEnumberable으로 변환됩니다.

+0

그게 나에게도 좋을 듯 하네. 이걸 없애기 위해 내가 가지고있는 것에 대한 수정을 권할 수 있겠 니? .edmx 변경 또는 저장소 구현 변경입니까? – Tommy

+0

알았어 - 나는 이것을 줄 것이며 결과가 무엇인지 보게 될 것이다. 나는 아직도 그것이 데이터 셋의 다중 열거를하고있는 이유에 관해서 난처한 상황이다. – Tommy

+0

Well spotted, Brian :) –

1

@SLC는 다음과 같이 말했습니다. EF가 생성하는 SQL을 살펴 봐야합니다. 놀랄 것입니다.

LINQPad을 사용하는 것이 좋습니다. 무료 및 유료 버전이 있습니다.

가장 좋아하는 것은 데이터 레이어 어셈블리를 가져 와서 모델에 대해 LINQ 문을 쓸 수 있다는 것입니다. 다른 쿼리 접근법을 쉽게 테스트 할 수 있습니다.


수정은 Find 대신 IEnumerable에서 IQueryable를 반환으로 쉽게 될 수 있습니다.

+0

그래, .Find() 메서드를 변경하려고했지만 this.objectSet.Where (predicate)의 메서드 서명이 IEnumerable을 반환하는 것으로 보입니다. Intellisense는 IQueryable을 보여줍니다. 어디에서,하지만 IEnumerable을 사용하는 이유가 표시되지 않습니다. – Tommy

+1

이상한! [.AsQueryable()] (http://msdn.microsoft.com/en-us/library/bb353734.aspx)을 추가하여 항상 강제로 적용 할 수 있습니다. –

+0

Ok - 알아 냈지만 여전히 동일한 동작을 봅니다. .Find()는 다음을 사용하여 IQueryable 을 반환합니다. public IQueryable Find (Func predicate) {return this.objectSet.Where (predicate) .AsQueryable();} – Tommy