Pytanie Przekazanie encji do implementowanego interfejsu w ogólnej metodzie z wykorzystaniem LINQ dla Entity Framework


Mam ogólnej metody kwerendy obiektów typu TEntity w EF. Chcę dodać warunek jako klauzula where, jeśli TEntity implementuje określony interfejs. Moja metoda to:

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 <> () to metoda, która po prostu zwraca wartość true / false, jak sama nazwa wskazuje.

Kiedy uruchomię to dla adresu podmiotu, który implementuje IDeletableEntity, pojawia się błąd:

Nie można rzutować typu "Adres", aby wpisać "IDeletableEntity". LINQ to Entities obsługuje tylko prymitywne lub wyliczeniowe typy EDM do rzutowania.

Jakieś pomysły, w jaki sposób mogę obejść to ograniczenie LINQ?


11
2018-03-23 20:30


pochodzenie




Odpowiedzi:


To jest działające rozwiązanie:

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;
}

12
2018-03-23 20:57



To nie może działać, jeśli TPredicateWellKnownType nie można rzutować na TEntity ... i dlatego dostaję wyjątek od tego efektu - War
Absolutny 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. - Chris Schaller
Gdzie IsImplementationOf pochodzi z ? - koryakinp
Nie jestem pewien, jak to działa, ale tak. Dzięki. - Christopher Townsend


Jeśli wszystkie DbSets ma właściwość "UserID", utwórz kolejny interfejs o nazwie "IUserID", a następnie spróbuj tego kodu:

    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
2018-01-16 16:50



Teraz wykonujesz całą operację w pamięci, a nie w bazie danych. - Servy
@Servy Można to rozwiązać, usuwając metodę .ToList (), prawda? - Junior
Musisz również upewnić się, że reszta kodu działa poprawnie, gdy jest uruchamiana przez dostawcę EF, ale tak, ToList zmusi kod do wszystkich uruchomionych w pamięci. - Servy


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.

Problem z odlewaniem LINQ-do-podmiotu

zaproponował to rozwiązanie.

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

-2
2018-03-23 20:37



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
Ponownie, jak powiedziałem Vladimirowi, jak to możliwe, że działa, chyba że możliwe jest wyłapanie IDellable do T - War