2017-04-24 48 views
5

Jestem zaskoczony przez EF 6 .... Mam aplikację internetową, która zachowuje się bardzo źle pod względem wydajności. Analizując, odkryłem, że jeden z winowajców jest moją metodą, która sprawdza, czy kolekcja jest pusta (lub nie) w jednostce EF6.EF6 - nie robię tego, czego się spodziewałem. Any()

Zasadniczo mam:

public partial class BaseEntity 
{ 
    public int BaseEntityId { get; set; } 
    public string Name { get; set; } 

    // a few more properties, of no concern here.... 

    // a lazily loaded collection of subitems   
    public virtual ICollection<Subitem> Subitems { get; set; } 
} 

public partial class Subitem 
{ 
    public int SubitemId { get; set; } 
    public int BaseEntityId { get; set; } 
    public string Name { get; set; } 

    // a few more properties, of no concern here.... 
} 

W mojej aplikacji, muszę sprawdzić, czy dana instancja BaseEntity jest „pusty” - który jest zdefiniowany jako nie mający podelementy. Więc dodałem tę metodę CheckIfEmpty do drugiego częściowego pliku klasy:

public partial class BaseEntity 
{ 
    public bool IsEmpty 
    { 
     return !Subitems.Any(); 
    } 
}   

Teraz pojedynczy BaseEntity może mieć setki lub tysiące podpunktach - więc chciałem użyć najbardziej skuteczny sposób, by sprawdzić, czy nie było żadnych podelementy . Moja założenie, że nazywając .Any() na zbiór, który nie został jeszcze załadowany z bazy będzie zasadniczo przekładają się na wezwanie

IF EXISTS(SELECT * FROM dbo.Subitems) ...... 

SQL - lub coś wzdłuż tych linii - po prostu sprawdzić, czy istnieją jakieś elementy - albo nie. Specjalnie wybrałem .Any() przez .Count > 0, ponieważ wiem, że sprawdzanie licznika będzie wymagało wyliczenia całej kolekcji, a zatem jest wysoce nieefektywne, gdy chcę tylko wiedzieć, czy (lub nie) istnieją jakieś elementy.

I nie trzeba znać ile istnieją, ani też nie jestem zainteresowany w szczegółach - wystarczy prosty TAK lub NO do is empty? pytanie byłoby wystarczające.

Ku mojemu wielkiemu zdumieniu (i oszołomieniu) okazuje się, że EF6 zmienia to proste połączenie .Any() w instrukcję SELECT, która ładuje całą kolekcję ! - to zdecydowanie NIE czego się spodziewali ......

Więc jest jakiś prosty sposób, aby po prostu sprawdzić jeśli nie-jeszcze-załadowany kolekcja ma żadnych wartości - lub nie - BEZ ładowanie całej kolekcji z bazy danych?

+0

Wywołanie 'Subitems' ładunki wszystkie elementy jeszcze przed' . Any został osiągnięty, w ten sposób zaimplementowano leniwy ładowanie. Przy pierwszym dostępie do 'Subitems' jest on ładowany. Staram się nigdy nie używać leniwego ładowania, ale komplikuje to mój kod: staram się zamknąć kontekst tak szybko, jak to możliwe, i otworzyć kolejny inny dla 'db.Subitem.Any (si => si. BaseEntityId == .. .) ' – Kobi

+0

' SubItems' to 'ICollection'. Może działać tak, jak się spodziewasz, jeśli jest to 'IQueryable' (nie jestem pewien, i nie wiem, czy leniwy ładowanie może działać z' IQueryable') –

+1

Korzystając z podejrzanego trybu ładowania i wysyłając zapytanie do DbSet, Uzyskaj to, czego potrzebujesz: context.Set ().Każdy(); przekłada się na: SELECT SPRAWY gdy jest ( SELECT 1 OD [TEntity] CO [M]), następnie wylewa się (1 CO BIT) else węglowej (0 jako bit) END – alessalessio

Odpowiedz

5

Dzięki zastosowaniu zachłannego ładowania i odpytywanie DbSet, masz co chcesz:

context.Set<TEntity>().Any();

przekłada się na:

SELECT CASE WHEN EXISTS (SELECT 1 FROM [TEntity] AS [m]) THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END 
+0

Jeszcze raz dziękuję - świetna odpowiedź. Zmodyfikowałem go nieznacznie, aby użyć 'context.Subitems.Any (si => si.BaseEntityId == 42);' i to również działa bardzo ładnie. Świetne podejście - dzięki! –

+0

cieszę się, że pomogło. Twoje zdrowie – alessalessio