2017-02-03 4 views
0

"클라이언트"이라는 엔터티와 "카드"이라는 엔터티가 있습니다.EF6을 사용하여 상대 엔터티의 수를 얻는 방법

클라이언트에는 카드이 많이있을 수 있습니다.

public class Client{ 
    public virtual ICollection<Card> Cards {get; set;} 
} 

가 지금은 WPF에서 DataGrid에 클라이언트에게 데이터를 보여주고 싶은, 나는 카드 데이터를 카운트 싶어, 그래서 추가

클라이언트는 엔티티는 다음과 같습니다

public class Client{ 
    public virtual ICollection<Card> Cards {get; set;} 

    public int CardCount 
    { 
     return Cards.Count; 
    } 
} 

그리고 다음을 나는 데이터 w를 조회 : 같은 클라이언트 엔티티에 속성 나는 응용 프로그램을 실행하면 Linq에와 바인딩이

var query = from n in db.Clients select n; 

을 볼 수 i 번째, 난 그냥 바로 return Cards.Count; 줄에 예외를 얻었다;

System.ObjectDisposedException 
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. 

그래서 내가 어떻게 제대로 카드을 계산 얻을 수 있을까?

답변

1

는 다른 답변이 여기에 보여보다 방법이 간단한 방법이있다.

var client = db.Clients.FirstOrDefault(c=> c.Id = someid); //get a client 
if (client != null) 
{ 
    cardCount = client.Cards.Count; 
} 

과 같은 솔루션을 사용하면 N + 1 문제 선택 문제가 발생할 수 있습니다. 관심이 있다면 읽으십시오. 그러나 간단히 말해 다음과 같은 의미입니다.

정확히 하나의 정확한 클라이언트에만 관심이 있지만 N 개의 클라이언트를 표시하려면 1 개의 쿼리를 수행해야합니다. 그냥 고객을 확보하십시오. 그런 다음 FirstOrDefault 작업을 수행하면 Client 레코드 당 데이터베이스에 대해 추가로 데이터베이스 왕복 (1 회)을 수행하므로 추가로 N * 1 = N 왕복이 발생합니다. 즉, 관련된 데이터없이 Clients을 쿼리하는 경우 한 번의 쿼리만으로 많은 클라이언트 레코드를 얻을 수 있습니다.그러나 각각에 관련 데이터를 하나씩 가져 와서 DB 라운드 트립을 너무 많이하고 있습니다.

여기 조인과 예측을 사용하여이 문제를 해결할 수 있습니다. 단일 데이터베이스 액세스에서 필요한 모든 데이터를 얻을 수 있습니다.

using (var context = GetDbContext()) 
{ 
    return context.Clients.Select(cli => new YourViewModel 
    { 
     Name = cli.FullName, 
     // Other prop setters go here 
     CardCount = cli.Cards.Count 
    }).Skip((page - 1) * pageSize).Take(pageSize).ToList(); 
} 

어쨌든 차이점은 무엇입니까? 자, 여기에서는 구체화 된 객체로 작업하지 않습니다. 다른 객체는 여기에서 호출하지만 DbContext으로 호출합니다. 적절한 LINQ 연산자를 적용하면 (참고로,이 작업은 DbContext뿐만 아니라 IQueryable과 함께 작동합니다 (이미 메모리에있는 컬렉션에서 AsQueryable()을 호출하는 경우는 분명하지 않지만)), LINQ to Entities는 적절한 SQL을 사용하여 테이블을 결합하고 결과를 투영하므로 필요한 모든 데이터를 한 번에 가져올 수 있습니다. LINQ to Entities는 cli.Cards.Count을 적절한 SQL Count 문으로 변환 할 수 있습니다.

+0

또한 여기에 문제가 있는데, 클라이언트 엔터티 인스턴스 바인딩을 직접 Datagrid에 사용합니다. 클라이언트 엔터티에 속성이없는 경우 어떻게 CardsCount 열 데이터를 표시합니까? –

+0

간단하지 않아야합니다. 쿼리 대상과 방법을 적절하게 제어하려면 데이터에 적합한 래퍼를 구현해야합니다. 이는 성능을 적절하게 제어 할 수있는 유일한 방법입니다. 그러나 자신이 소품에 접근 할 필요가없고 그 결과와 관련된 유일한 것이'DataGrid'라면, 익명의 타입을 사용하여 결과를 * 비 전형적인 * 콜렉션으로 반환 할 수 있습니다. 'DataGrid'도 바인딩 할 수 있습니다. –

+0

시도해보고 예외가 발생했습니다 : '건너 뛰기'메서드는 LINQ to Entities의 정렬 된 입력에 대해서만 지원됩니다. –

-1

검색된 쿼리를 구체화하지 않으면 ObjectDisposedException이 표시됩니다. 다음과 같은 경우 쿼리는 GetNonMaterialized에서 처음으로 액세스하고 메서드를 끝내기 전에 액세스 할 때만 실행됩니다. 사실 db은 범위가 없어져 폐기되었습니다.

public IEnumerable<Client> GetNonMaterialized() 
{ 
    return from n in db.Clients select n; 
} 

다음과 같은 경우에는 메소드를 종료하기 전에 쿼리가 실행됩니다.

public IEnumerable<Client> GetMaterialized() 
{ 
    return (from n in db.Clients select n).ToList(); 
} 

항상 조회가 ObjectContext의 범위를 종료 before 실행되어 있는지 확인합니다.

쿼리가 실행되는지 여부와 EF의 로그 로깅 여부를 알고 싶다면.

How can I turn off Entity Framework 6.1 logging?

+0

어디에서 GetMaterialized 메서드를 배치해야합니까? 어떻게 사용할 수 있습니까? WPF가 처음이므로 좀 더 자세히 설명해주십시오. 감사합니다 ~ –

+0

이것은 작동하지 않습니다. ToList를 호출하면 비 게으적 속성 만 채워지게되고 관련된 지연된 개체 나 컬렉션은로드되지 않습니다. 두 코드 조각의 유일한 차이점은 컨텍스트가 액세스 전에 삭제 된 경우 첫 번째로는 레이지가 아닌 프로 시저를 가져올 수 없지만 두 번째 코드에서는 적어도 기본 속성을 얻을 수 있다는 것입니다. –

0

이 같은 엔티티를로드하지 않고 수를 얻을 수 있습니다 :

using (var context = new MyContext()) 
{ 
    var client = context.Client.Find(clientId); 

    // Count how many cards the client has 
    var cardsCount = context.Entry(client) 
         .Collection(b => b.Cards) 
         .Query() 
         .Count(); 
} 

더 많은 정보 on MSDN page합니다.

+0

그는 'DataGrid'를 사용하기 때문에 나는 여러 클라이언트의 세부 정보를 표시 할 수 있습니다.이 경우 선택 n + 1 문제가 발생합니다. –

+0

이것은 뷰 모델에서 사용할 수 있으며 거기에 카드 수를 표시합니다. 엔티티 자체에서 그 속성을 갖는 것이 좋은 디자인이라고 생각하지 않습니다. –

+0

viewModel에서 가져 오는 것도 좋은 생각이 아닙니다. viewModel에는 정의에 의한 * 논리가 포함되지 않아야합니다. 그리고 당신이 이것을 어디에서하는지에 상관없이, 데이터베이스로의 다중 라운드 트립을 피할 수있는 유일한 장소는이 값을 다른 데이터와 함께 선택하는 것입니다.엔티티 내부도 viewmodel도 아니므로 select n + 1을 방지 할 수 없습니다.이 말은 제가 여러분에게하려고하는 것입니다 ... –