2015-03-23 10 views
10

Mam ogólną metodę sprawdzania obiektów typu TEntity w EF. Chcę dodać warunek jako klauzula where, jeśli TEntity implementuje określony interfejs. Metoda mam to:Cast Entity do implementowanego interfejsu w ogólnej metodzie Używanie LINQ dla Entity Framework

public TEntity GetByUserID(Guid userID) 
{ 
    var query = this.DbSet; 
    if (typeof (TEntity).IsImplementationOf<IDeletableEntity>()) 
    { 
     query = query 
      .Where((x => !((IDeletableEntity)x).IsDeleted); 
    } 
    return query 
     .FirstOrDefault(x => x.UserID == userID); 
} 

IsImplementationOf <>() jest metodą, która po prostu zwraca true/false jak sama nazwa wskazuje.

Gdy uruchomię to na adres jednostki realizujące IDeletableEntity, otrzymuję błąd:

Unable to cast the type 'Address' to type 'IDeletableEntity'. LINQ to Entities only supports casting EDM primitive or enumeration types.

Jakieś pomysły jak mogę przejść wokół tego ograniczenia LINQ?

Odpowiedz

-2

Myślę, że problemem może być bezpośredni rzut, który robisz w swoim wyciągu, a także zapytanie może implementować typy wyliczeniowe, dzięki czemu identyfikacja nie jest zaimplementowana jak na jednym obiekcie.

LINQ-to-entities casting issue

proponuje to rozwiązanie.

return query.ToList() 
         .Cast<IDeletable>() 
         .Where(e => e.Deleted) 
         .Cast<T>(); 
+0

ten prawdopodobnie zadziała, ale najpierw przejdzie do bazy danych i pobierze wszystkie elementy, wyrzuci je do listy, a następnie odfiltruje w pamięci. Rozwiązanie, które znalazłem wysyła warunek do bazy danych, tzn. Otrzymujesz tylko elementy z poprawną wartością flagi. – Vladimir

+0

Ponownie, jak powiedziałem Vladimirowi, jak to możliwe, chyba że możliwe jest wyłapanie IDeletable do T – War

10

Jest to roztwór roboczy:

public TEntity GetByUserID(Guid userID, params Include<TEntity>[] includes) 
{ 
    var query = this.DbSet; 
    query = Where<IDeletableEntity>(query, x => !x.IsDeleted); 
    return query 
     .FirstOrDefault(x => x.UserID == userID); 
} 

public static IQueryable<TEntity> Where<TPredicateWellKnownType>(IQueryable<TEntity> query, Expression<Func<TPredicateWellKnownType, bool>> predicate) 
{ 
    if (typeof(TEntity).IsImplementationOf<TPredicateWellKnownType>()) 
    { 
     query = ((IQueryable<TPredicateWellKnownType>)query) 
      .Where(predicate) 
      .Cast<TEntity>(); 
    } 
    return query; 
} 
+0

To nie może zadziałać, jeśli TPredicateWellKnownType nie może być rzutowany na TEntity ... i dlatego dostaję wyjątek od tego efektu. – War

+0

Absolute geniusz! Problem, który miałem, polegał na znalezieniu składni, aby umożliwić kompozytorowi zarządzanie rzutowaniem bez próby przekazania tej wiedzy do silnika SQL i bez modyfikowania typu zapytania. Bardzo eleganckie rozwiązanie dla czegoś, co wydaje się oczywiste, gdy widzimy, że zrobiono to dla nas, ale wydawało się zbyt brzydkie, aby spróbować siebie. –

-1

Jeśli wszystkie DbSets ma własność 'userid' następnie utworzyć inny interfejs o nazwie 'IUserID', a następnie wypróbować ten kod:

protected TEntity GetByUserID<TEntity>(Guid userID) where TEntity : class 
    { 
     var user = this.Set<TEntity>() 
      .ToList() 
      .Cast<IDeletableEntity>() 
      .Where(u => (!u.IsDeleted)) 
      .Cast<IUserID>() 
      .Where(u => (u.UserID == userID)) 
      .FirstOrDefault(); 
     return user; 
    } 
+1

Teraz wykonujesz całą operację w pamięci, a raczej w bazie danych. – Servy

+0

@Servy Można to rozwiązać, usuwając metodę .ToList(), prawda? – Junior

+0

Musisz także upewnić się, że reszta kodu działa poprawnie po uruchomieniu przez dostawcę EF, ale tak, 'ToList' wymusi na kodzie uruchomienie wszystkich w pamięci. – Servy

Powiązane problemy