Analyzer warning when Enumerable LINQ operator is accidentally used on an EF Core IQueryable source
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
### What problem are you trying to solve?
I would like to report a subtle issue where an EF Core query can accidentally switch from System.Linq.Queryable to System.Linq.Enumerable when a Func<> delegate is used instead of an Expression>.
This can happen when a predicate is stored in a variable typed as Func and then used in a query against a DbSet / IQueryable source.
Minimal example:
Func predicate = e => e.Salary > 3000;
var employees = context.Employees
.Where(predicate)
.ToList();
In this case, the call resolves to:
System.Linq.Enumerable.Where(
IEnumerable source,
Func predicate)
instead of:
System.Linq.Queryable.Where(
IQueryable source,
Expression> predicate)
Since DbSet implements IQueryable but is also enumerable, the code compiles successfully. However, the predicate is not part of the EF Core expression tree and therefore cannot be translated to SQL.
As a result, a developer may incorrectly assume that the predicate is translated and executed in the database, while the filtering actually happens on the client side after the data is enumerated.
Currently, no warning is produced for this scenario.
This can be especially confusing for developers who are learning EF Core, because the query looks like a normal EF Core LINQ query but behaves differently due to overload resolution.
### Describe the solution you'd like
I would like EF Core analyzers to produce a warning when a System.Linq.Enumerable LINQ operator is invoked on a source whose type is, or implements, IQueryable.
For example, the analyzer could detect cases like:
IQueryable source = context.Employees;
Func predicate = e => e.Salary > 3000;
var employees = source
.Where(predicate)
.ToList();
Suggested warning message:
Possible unintended client-side evaluation.
The LINQ method 'Where' is using a Func<> delegate on an IQueryable source.
This causes System.Linq.Enumerable.Where to be used, so the predicate is evaluated on the client instead of being translated to SQL.
Use Expression> or an inline lambda expression to allow EF Core to translate the query.
Suggested fix:
Expression> predicate = e => e.Salary > 3000;
var employees = context.Employees
.Where(predicate)
.ToList();
or:
var employees = context.Employees
.Where(e => e.Salary > 3000)
.ToList();
The analyzer could initially target common LINQ operators such as Where, Select, OrderBy, Any, Count, FirstOrDefault, and similar methods when the selected method comes from System.Linq.Enumerable and the source is an IQueryable.
To reduce false positives, the analyzer should probably not warn when the developer explicitly switches to client-side evaluation, for example:
context.Employees
.AsEnumerable()
.Where(predicate);
or after materialization:
context.Employees
.ToList()
.Where(predicate);
This proposal is specifically about analyzer support. EF Core itself may not be able to produce a runtime warning in this scenario because the Func<> predicate is not part of the expression tree that EF Core receives.
Contributor guide
Research direction
No files or tests are named; start by locating EF Core's analyzer entry points and existing LINQ diagnostics. Define the diagnostic and test coverage around Enumerable operators selected for IQueryable sources, while confirming that explicit AsEnumerable() and materialized sources are excluded; done means the proposed warning and safe alternatives are covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- databases, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100