ForWiz
ForWiz

Reputation: 145

The LINQ expression could not be translated (Generic class)

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

Answers (1)

StriplingWarrior
StriplingWarrior

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

Related Questions