Mam następującą ogólną metodę rozszerzenia:
public static T GetById<T>(this IQueryable<T> collection, Guid id)
where T : IEntity
{
Expression<Func<T, bool>> predicate = e => e.Id == id;
T entity;
// Allow reporting more descriptive error messages.
try
{
entity = collection.SingleOrDefault(predicate);
}
catch (Exception ex)
{
throw new InvalidOperationException(string.Format(
"There was an error retrieving an {0} with id {1}. {2}",
typeof(T).Name, id, ex.Message), ex);
}
if (entity == null)
{
throw new KeyNotFoundException(string.Format(
"{0} with id {1} was not found.",
typeof(T).Name, id));
}
return entity;
}
Niestety Entity Framework nie wie, jak obsłużyć, predicate
ponieważ C # przekonwertował predykat na następujący:
e => ((IEntity)e).Id == id
Entity Framework zgłasza następujący wyjątek:
Nie można rzutować typu „IEntity” na typ „SomeEntity”. LINQ to Entities obsługuje tylko rzutowanie typów podstawowych lub wyliczeniowych EDM.
Jak możemy sprawić, by Entity Framework działał z naszym IEntity
interfejsem?
Dodatkowe wyjaśnienia dotyczące
class
„poprawki”.Ta odpowiedź pokazuje dwa różne wyrażenia, jedno z, a drugie bez
where T: class
ograniczeń. Bezclass
ograniczeń mamy:iz ograniczeniem:
Te dwa wyrażenia są różnie traktowane przez strukturę encji. Patrząc na źródła EF 6 , można stwierdzić, że wyjątek pochodzi stąd, patrz
ValidateAndAdjustCastTypes()
.Dzieje się tak, że EF próbuje rzutować
IEntity
na coś, co ma sens w świecie modelu domeny, jednak nie udaje się to, dlatego zgłaszany jest wyjątek.Wyrażenie z
class
ograniczeniem nie zawieraConvert()
operatora, rzutowanie nie jest wypróbowywane i wszystko jest w porządku.Wciąż pozostaje otwarte pytanie, dlaczego LINQ buduje różne wyrażenia? Mam nadzieję, że jakiś kreator C # będzie w stanie to wyjaśnić.
źródło
Entity Framework nie obsługuje tego po wyjęciu z pudełka, ale
ExpressionVisitor
można łatwo napisać wyrażenie, które tłumaczy:Jedyne, co musisz zrobić, to przekonwertować przekazany predykat za pomocą gościa wyrażenia w następujący sposób:
Innym - mało elastycznym - podejściem jest wykorzystanie
DbSet<T>.Find
:źródło
Miałem ten sam błąd, ale podobny, ale inny problem. Próbowałem utworzyć funkcję rozszerzającą, która zwróciła IQueryable, ale kryteria filtru były oparte na klasie bazowej.
W końcu znalazłem rozwiązanie, które było dla mojej metody rozszerzającej do wywołania .Select (e => e jako T), gdzie T jest klasą potomną, a e jest klasą bazową.
pełne szczegóły są tutaj: Utwórz rozszerzenie IQueryable <T> przy użyciu klasy bazowej w EF
źródło