2013-10-15 8 views
10

Mam tabelę NxN, wyobraź sobie:LINQ podmiotom Dołącz + Gdzie Metoda

Użytkownika (id, ...) < - UserAddresses (id, USERID addressId, włączona, ...) -> Adresy (Id , ...)

UserAddresses zawiera FK do użytkownika i adresu. Co wiem, Entity utworzone przez Entity Framework User, zawiera kolekcję dla UserAddresses. Adres zawiera kolekcję do UserAddresses, a określony adres użytkownika zawiera jedno refenrece do użytkownika i do jednego adresu.

Teraz chcę zrobić następne zapytanie linq. Dla określonego identyfikatora użytkownika, tylko userAddresses z włączoną flagą ustawioną na true. Dla określonego identyfikatora użytkownika, userAddresses może zawierać wiele wpisów, ale tylko jeden jest ustawiony dla tego konkretnego użytkownika.

Zapytanie mogę zrobić, to:

context.User.Include(x => x.UserAddresses) 
      .Include(x => x.UserAddresses.Select(y => y.Address)) 
      .Single(x => x.id == USER_ID) 

ale to, co naprawdę chcę to nie załadować wszystkie UserAddresses dla tego użytkownika ... Tylko jeden, który zawiera aktywne, setted PRAWDA!

Ktoś może mi pomóc w wykonaniu tego zapytania?

+0

Jakiego rodzaju "Uwzględnij" używasz? O ile mi wiadomo, metoda 'Include' z' System.Data.Objects.ObjectQuery' otrzymuje tylko 1 argument ciągu znaków? –

+0

@KingKing Zrobiłem metodę rozszerzenia, która formatuje wyrażenie lambda na string i przekazuje je do "natywnej" metody ObjectQuery include. – anotherNeo

+0

Jeśli tak, odpowiedź na twój problem jest "niemożliwe", "Uwzględnij" nie jest tak potężny, nie można wykonać żadnego "filtra" przed włączeniem. –

Odpowiedz

14

W EF nie ma możliwości częściowego załadowania właściwości powiązania. Spróbuj wybrać do typu anonimowego wziąć tylko to, czego potrzebujesz:

var result = context.User 
    .Where(u => u.Id == userId) 
    .Select(u => new { 
     Addresses = u.UserAddresses.Select(ua => ua.Address) 
      .Where(a => a.Enabled), 
     User = u // if you need this as well 
    }) 
    .Single(); 

To nie będzie ładować result.User.UserAddresses, ale result.Addresses będzie miał dokładnie to, co chcesz.

Jeśli naprawdę chcesz zwrócić wszystko jako część klasy Użytkownik, musisz odłączyć result.User, a następnie zaktualizować wynik.User.UserAddresses, aby wskazać result.Addresses.

+0

Muszę zwrócić podmiotowi User wszystkie powiązane elementy, ale nie wszystkie zbiory są zmapowane. Więc myślę, że anonimowy typ jest nieprawidłowy:/ – anotherNeo

+0

@goncaloRD: Niestety warunki 'where' nie są możliwe przy użyciu' Include''. To jest twoja najlepsza opcja. – Ocelot20

+0

@ChaseMedallion dzięki, To jest dokładnie to, czego potrzebuję. Zapytanie jest wykonywane perfekcyjnie, a obiekty EF są mapowane tak, jak chcę. Utworzono typ, aby moje obiekty były typograficzne, do użycia w mojej Warstwie biznesowej. – anotherNeo

1

Innym alternatywnym rozwiązaniem jest użycie load() zamiast include():

var foundUser = context.User.Single(x => x.Id == USER_ID); 

context.Entry(foundUser).Collection(u => 
u.UserAddresses).Query().Where(userAddress => 
userAddress.Enabled).Load(); 

pamiętać, że obciążenia() metoda może być ignorowane przez EF w niektórych scenariuszach:

  1. jeśli używasz EF wraz z funkcją Leniwe ładowanie, pobieranie obiektu powoduje przeniesienie wszystkich powiązanych kolekcji, które zostały oznaczone jako Wirtualne w twojej klasie. Wykonując to, należy:

    context.User.Single (x => x.id == USER_ID);

    otrzymasz wszystkie UserAddresses powiązane z Użytkownikiem, chyba że wyłączysz Leniwe Ładowanie dla swojej kolekcji poprzez usunięcie Wirtualnego Słowa Kluczowego z właściwości w klasie Użytkownik.

  2. Jeśli dodajesz/usuwasz kolekcję UserAddresses w swoim programie i wywołuje kontekst context.SaveChanges(); bez pozbawiania kontekstu, przy następnym ładowaniu obiektu User, kolekcja UserAddresses zostanie załadowana z bufora kontekstu EF, a nie z DB (najnowsze zmiany). W takim przypadku musisz pozbyć się kontekstu i utworzyć nowy kontekst, zanim uzyskasz dostęp użytkownika do kontekstu. Na przykład, jeśli masz użytkownika z 5 elementami w kolekcji UserAddresses, a jeden z elementów jest wyłączony (item.Enabled = false), a następnie wywołaj context.SaveChanges() bez usuwania kontekstu, następnym razem, gdy otrzymasz obiekt User z tego samego kontekstu ma już 5 elementów w swojej kolekcji, które pochodzą z pamięci podręcznej kontekstu i ignoruje twoją metodę Load().

PS:

Lazy Loading funkcja jest włączona, jeśli wszystkie poniższe warunki stosowane:

  1. context.Configuration.LazyLoadingEnabled = true;
  2. context.Configuration.ProxyCreationEnabled = true;
  3. UserAddresses został zdefiniowany jako Virtual w twojej klasie User.