Supported LINQ Queries
This page is intentionally conservative.
It describes query shapes that are clearly exercised by the test suite today. If a LINQ shape is not listed here, do not assume it is supported just because it looks reasonable.
For the detailed maintainer evidence behind these claims, see the LINQ Translation Support Matrix.
Core Query Operations
The following operations are covered by tests:
ToList()Count()Count(predicate)Any()Any(predicate)Where(...).Any()Sum(...)Min(...)Max(...)Average(...)Join(...)Single(...)SingleOrDefault(...)First(...)FirstOrDefault(...)Last()LastOrDefault(...)OrderBy(...)OrderByDescending(...)ThenBy(...)ThenByDescending(...)Skip(...)Take(...)Select(...)
Supported Predicate Shapes
Equality and comparison
Test coverage exists for:
==!=>>=<<=
This includes:
- scalar comparisons
- enum comparisons
- nullable-boolean comparisons
- property-to-property comparisons such as
x.emp_no <= x.from_date.Day
Boolean composition
Test coverage exists for:
- chained
Where(...).Where(...)predicates over the same source &&||!- nested grouped predicates
Collection membership
The test suite covers Contains(...) against in-memory collections used as an IN (...) style predicate:
- arrays
List<T>HashSet<T>- the tested
ReadOnlySpan<T>shape - projected local sequences such as
localIds.Select(x => x.Value).Contains(row.Id.Value)when the projection is fully local
Empty local Contains(...) predicates are also covered in direct, negated, AND, and OR compositions. The translator treats those as fixed true/false conditions instead of emitting invalid IN () SQL.
Local Any(predicate) over in-memory collections is covered for equality-membership shapes that can safely become IN (...) or NOT IN (...):
ids.Any(id => id == row.Id)items.Any(item => item.Id == row.Id)- reversed equality such as
items.Any(item => row.Id == item.Id)
Empty local Any(predicate) has similar fixed-condition coverage. For empty local collections, Contains(...), Any(), and Any(predicate) become 1=0; negating those expressions becomes 1=1. The predicate body is not visited when an empty local sequence already decides the result. Compound local predicates are still not supported for non-empty collections; write those as an explicit local projection plus Contains(...) when the intent is membership.
String members
The test suite covers:
StartsWith(...)EndsWith(...)- string
Contains(...) ToUpper()ToLower()Trim()Substring(...)Lengthstring.IsNullOrEmpty(...)string.IsNullOrWhiteSpace(...)
Date and time member access
The test suite also covers member access inside predicates for several date and time types, including:
DateOnly.YearDateOnly.MonthDateOnly.DayDateOnly.DayOfYearDateOnly.DayOfWeekTimeOnly.HourDateTime.MinuteDateTime.SecondDateTime.Millisecond
Nullability
The test suite covers nullable-boolean comparisons such as:
x.IsDeleted == truex.IsDeleted == falsex.IsDeleted == nullx.IsDeleted != truex.IsDeleted != false
It also covers nullable value predicates such as:
x.last_login.HasValue!x.last_login.HasValue- guarded
.Valuecomparisons such asx.last_login.HasValue && x.last_login.Value == login - guarded nullable date/time member access such as
x.created_at.HasValue && x.created_at.Value.Minute == minute - mixed nullable/non-nullable equality and inequality such as
x.last_login == loginandx.last_login != login
For nullable != nonNullable, null rows are included, matching C# lifted nullable semantics.
Supported Relation Predicates
Generated one-to-many relation properties can be used in a narrow set of SQL-backed predicates. The translator emits a correlated EXISTS subquery instead of lazy-loading the relation for every candidate row.
The test suite covers:
parent.Children.Any()!parent.Children.Any(...)parent.Children.Any(child => child.Column == value)- simple related-row comparisons with
==,!=,>,>=,<, and<= - simple
&&and||groups inside the relation predicate - existence-equivalent counts such as
parent.Children.Count() > 0,Count() >= 1,Count() != 0,Count() == 0,Count() <= 0, andCount() < 1
Example:
var departments = db.Query().Departments
.Where(department => department.Managers.Any(manager => manager.emp_no == employeeNumber))
.ToList();
This first slice is intentionally not a relation traversal engine. These shapes are not supported yet:
- relation projections inside provider
Select(...) - relation aggregates other than the documented existence-equivalent
Count()comparisons - thresholds such as
Children.Count() > 1 - predicates that traverse another relation from the related row, such as
child.Parent.Name == value - many-to-one relation property predicates outside the one-to-many
Any(...)/existence pattern
Supported Projection Shapes
Select(...) projections are evaluated after DataLinq has translated SQL filtering, ordering, and paging and materialized the selected rows. They are not SQL SELECT-list expressions today. That is less efficient for wide rows than SQL-backed projection, but it keeps projection semantics honest: projection code runs as normal .NET code over the materialized model instance.
The test suite covers row-local projections such as:
- selecting the full model
- selecting a scalar property such as
Select(x => x.DeptNo) - selecting an anonymous type such as
Select(x => new { no = x.DeptNo, name = x.Name }) - computed scalar projections such as
Select(x => x.first_name + ":" + x.emp_no.Value) - computed anonymous projections using materialized member chains such as
Trim(),ToUpper(), andLength
Example:
var departments = db.Query().Departments
.OrderBy(x => x.DeptNo)
.Select(x => new
{
no = x.DeptNo,
name = x.Name
})
.ToList();
Relation-property projections are not supported in provider Select(...) yet. Load rows first with ToList() and then traverse relation properties explicitly so the extra relation queries are visible in your code.
Supported Explicit Joins
The test suite covers one narrow explicit inner Join(...) shape:
- one outer DataLinq query source
- one inner DataLinq query source
- direct member equality keys such as
outer.DepartmentIdandinner.Id - nullable
.Valuekey selectors such asemployee.emp_no.Value - a result selector that projects row-local values from both materialized rows
Example:
var rows = db.Query().DepartmentEmployees
.Join(
db.Query().Departments,
departmentEmployee => departmentEmployee.dept_no,
department => department.DeptNo,
(departmentEmployee, department) => new
{
departmentEmployee.emp_no,
department.Name
})
.ToList();
The implementation uses a SQL inner join to select primary keys from both sides, then materializes both rows through DataLinq caches and applies the result selector as normal .NET code. That keeps projection semantics consistent with regular Select(...), but it is not yet a general SQL projection engine.
These join shapes are not supported yet:
GroupJoin(...)- left/outer join patterns such as
DefaultIfEmpty() - composite anonymous-object join keys
- additional
Where(...),OrderBy(...), paging, or result operators over the joined result - relation-property joins or relation-property projections inside the result selector
Supported Scalar Aggregates
The test suite covers SQL-backed scalar aggregates over direct numeric member selectors:
Sum(x => x.Number)Min(x => x.Number)Max(x => x.Number)Average(x => x.Number)
Filtered aggregates are also covered:
var total = db.Query().Managers
.Where(x => x.dept_fk.StartsWith("d00"))
.Sum(x => x.emp_no);
Nullable numeric members are supported when the selector is the nullable member itself or its nullable .Value member:
var min = db.Query().Employees.Min(x => x.emp_no);
var sum = db.Query().Employees.Sum(x => x.emp_no!.Value);
Sum(...) returns zero for an empty filtered sequence. Nullable Min(...), Max(...), and Average(...) return null for an empty filtered sequence. Aggregates over computed selectors, grouped aggregates, and relation-property aggregates are not supported yet.
Supported Ordering and Paging
The test suite covers:
- single-column ordering
- multi-column ordering
- mixed ascending and descending ordering
Skip(...)Take(...)Skip(...).Take(...)
Example:
var page = db.Query().Employees
.Where(x => x.emp_no < 990000)
.OrderBy(x => x.birth_date)
.ThenBy(x => x.emp_no)
.Skip(5)
.Take(10)
.ToList();
Direct Primary-Key Lookup
There is also tested support for direct lookup without a LINQ predicate:
var department = Department.Get("d005", db);
That is useful when you already know the key and do not need a query pipeline.
Known Unsupported Operators
The test suite explicitly expects NotSupportedException for:
TakeLast(...)SkipLast(...)TakeWhile(...)SkipWhile(...)
Practical Advice
- If you care which row is "first" or "last", order explicitly first. Anything else is fake determinism.
Last()andLastOrDefault()are supported in tested scenarios, but they are not the query engine's nicest path. If what you mean is "top by X", writeOrderByDescending(...).First()instead.Any(predicate)is covered for straightforward cases. If a more elaborateAny(predicate)feels clever, flatten it intoWhere(...).Any()unless you have a test proving your exact shape works.- Prefer query shapes already covered by tests. That is the brutally honest rule. Unsupported LINQ in ORMs does not fail gracefully; it usually fails late and irritates you.
- Unsupported translation should surface as
QueryTranslationExceptionwith the unsupported method, operator, selector, or predicate expression in the message. - If you are writing a new query shape and you are not sure whether it is supported, add a test first and then document it.
Not Yet Documented as Supported
The current docs do not claim support for these because this pass has not verified them rigorously enough:
GroupBy(...)GroupJoin(...), outer joins, composite-key joins, and additional filtering/ordering/paging over joined results- aggregate operators over computed selectors, grouped aggregates, or relation properties
- relation-property projections inside provider
Select(...)and relation traversal inside relation predicates - broader client-side method translation inside SQL predicates beyond the string members listed above
That does not automatically mean they are impossible. It means the docs should not lie about them.