Reputation: 145
I've seen a dozen of these questions here on SO, but none of them were really of any help to me, so I decided to post another one, to try and fix my issue.
I have a class GenericRepository<T>
which serves as a template for basic CRUD
methods, because I have a relatively big model package, so I didn't want to implement the same method for different classes over and over again (Don't repeat yourself).
The problem arose when I tried to create a custom method for querying using a Predicate<T>
.
Here is my code:
public IEnumerable<T> GetMatching(Predicate<T> condition)
=> dbContext.Set<T>.Where(entity => condition(entity));
In my mind, the purpose of this was to be able to pass any kind of a query, to have it evaluated and given back as results.
Here is the part where I tried to fetch some Doctor
objects by filtering them using a field named EmployeeID
:
Repository.GetMatching(doctor => doctor.EmployeeID > 10);
The idea was, to give me back a list of doctors, which have their EmployeeID
number field greater than 10.
Unfortunately, this gave me the following error:
Unhandled exception. System.InvalidOperationException: The LINQ expression 'DbSet<Doctor>
.Where(d => Invoke(__condition_0, d[Doctor]))' could not be translated.
Either rewrite the query in a form that can be translated, or switch to client
evaluation explicitly by inserting a call to either
AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
According to the error message, I tried to add ToList()
at the end of the GetMatching()
function, but to no awail, it still failed with the same error message.
What am I missing?
Upvotes: 1
Views: 902
Reputation: 156534
Instead of a Predicate
take an Expression<Func<T, bool>>
, and then pass that in directly:
public IEnumerable<T> GetMatching(Expression<Func<T, bool>> condition)
=> dbContext.Set<T>.Where(condition);
The reason for this is that Entity Framework relies on expression trees to determine how to translate LINQ code into a SQL query. When the expression it's evaluating (the lambda you pass in to Where) calls a function that Entity Framework doesn't know how to translate (like an arbitrary delegate that someone passed into your function), it chokes.
On a side note, be thoughtful about the fact that returning an IEnumerable<T>
will make any additional LINQ code added on to the returned value get treated as LINQ-to-Objects, rather than being evaluated by Entity Framework. If this is what you're going for, consider calling .ToList()
and returning something like IReadOnlyCollection<>
to avoid delaying execution unnecessarily. If not, consider returning IQueryable<>
.
Upvotes: 1