Skip to content

Introduction

ExpressiveSharp is a Roslyn source generator that enables modern C# syntax in LINQ expression trees. It generates Expression<TDelegate> factory code at compile time, so you can use null-conditional operators, switch expressions, pattern matching, and more in queries that target EF Core or any LINQ provider.

The Two Problems

When using C# with LINQ providers like EF Core, you hit two walls:

1. Expression tree syntax restrictions

You write a perfectly reasonable query and hit:

error CS8072: An expression tree lambda may not contain a null propagating operator

Expression trees (Expression<Func<...>>) only support a restricted subset of C#. There is no ?., no switch expressions, no pattern matching. You end up writing ugly ternary chains instead of the clean code you would write anywhere else. For the full story of why this restriction has persisted since 2007, see The Expression Tree Problem.

2. Computed properties are opaque to LINQ providers

You define public string FullName => FirstName + " " + LastName and use it in a query, but EF Core cannot see inside the property getter. It either throws a runtime translation error, or worse, silently fetches the entire entity to evaluate FullName on the client (overfetching). The only workaround is to duplicate the logic as an inline expression in every query that needs it.

How ExpressiveSharp Works

ExpressiveSharp fixes both problems with a two-phase design:

Compile time (source generation)

Two Roslyn incremental source generators analyze your code:

  1. ExpressiveGenerator finds members decorated with [Expressive], validates them, and emits Expression.* factory code that builds the equivalent expression tree. These are registered in a per-assembly expression registry.

  2. PolyfillInterceptorGenerator uses C# 13 method interceptors to rewrite ExpressionPolyfill.Create() calls and IRewritableQueryable<T> LINQ method calls, converting delegate lambdas into expression trees at their call sites.

Runtime (expression expansion)

When you query with EF Core (via UseExpressives()) or call .ExpandExpressives() manually, an ExpressionVisitor walks the query tree and replaces opaque [Expressive] member accesses with the pre-built expression trees. Transformers then adapt the trees for the target provider (stripping null-conditional patterns for SQL, flattening blocks, etc.).

Your LINQ query
    -> [Expressive member accesses replaced with generated expressions]
    -> [Transformers adapt for SQL provider]
    -> Expanded expression tree
    -> EF Core SQL translation
    -> SQL query

All expression trees are generated at compile time. There is no runtime reflection or expression compilation.

Which API Should I Use?

Mark computed properties and methods with [Expressive] to generate companion expression trees. Then choose how to wire them into your queries:

ScenarioAPI
EF Core -- modern syntax + [Expressive] expansion on DbSetExpressiveDbSet<T> (or UseExpressives() for global expansion)
Any IQueryable -- modern syntax + [Expressive] expansion.WithExpressionRewrite()
EF Core -- SQL window functions (ROW_NUMBER, RANK, etc.)WindowFunction.* (install RelationalExtensions package)
Advanced -- build an Expression<T> inline, no attribute neededExpressionPolyfill.Create
Advanced -- expand [Expressive] members in an existing expression tree.ExpandExpressives()
Advanced -- make third-party/BCL members expressable[ExpressiveFor]

Comparison with Similar Libraries

FeatureExpressiveSharpProjectablesExpressionifyLinqKit
Source generator basedYesYesYesNo
Works with entity methodsYesYesYesPartial
Works with extension methodsYesYesYesYes
Composable membersYesYesNoPartial
Constructor projectionsYesYesNoNo
Null-conditional ?. rewritingYesYesNoNo
Switch expressions / pattern matchingYesYesNoNo
Block-bodied membersYes (experimental)Yes (experimental)NoNo
Enum method expansionYesYesNoNo
Inline expression creationYes (ExpressionPolyfill.Create)NoNoNo
Modern syntax in LINQ chainsYes (IRewritableQueryable<T>)NoNoNo
SQL window functionsYes (RelationalExtensions)NoNoNo
String interpolation supportYesNoNoNo
Tuple literals supportYesNoNoNo
List patterns / index-rangeYesNoNoNo
with expressions (records)YesNoNoNo
Collection expressionsYesNoNoNo
Customizable transformer pipelineYesNoNoNo
Plugin architecture (EF Core)YesNoNoNo
External member mappingYes ([ExpressiveFor])NoNoNo
Not coupled to EF CoreYesNoNoNo
Roslyn analyzers and code fixesYesYesNoNo

Requirements

.NET 8.0.NET 10.0
ExpressiveSharpC# 12C# 14
ExpressiveSharp.EntityFrameworkCoreEF Core 8.xEF Core 10.x
ExpressiveSharp.EntityFrameworkCore.RelationalExtensionsEF Core 8.xEF Core 10.x

Next Steps

Released under the MIT License.