Skip to content

Repository files navigation

ExpressiveSharp

CIcodecovNuGetNuGet DownloadsLicense: MIT.NETGitHub StarsGitHub Issues

Documentation | Getting Started

Source generator that enables modern C# syntax in LINQ expression trees. All expression trees are generated at compile time with minimal runtime overhead.

The Problem

There are two problems when using C# with LINQ providers like EF Core:

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# — no ?., no switch expressions, no pattern matching. So you end up writing ugly ternary chains instead of the clean code you'd write anywhere else. (Why hasn't this been fixed?)

2. Computed properties are opaque to LINQ providers. You define public string FullName => $"{FirstName} {LastName}" and use it in a query — but EF Core can't 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.

ExpressiveSharp fixes this. Write natural C# and the source generator builds the expression tree at compile time:

publicclassOrder{publicintId{get;set;}publicdoublePrice{get;set;}publicintQuantity{get;set;}publicCustomer?Customer{get;set;}// Computed property — reusable in any query, translated to SQL[Expressive]publicdoubleTotal=>Price*Quantity;// Switch expression — normally illegal in expression trees[Expressive]publicstringGetGrade()=>Priceswitch{>=100=>"Premium",>=50=>"Standard",
_ =>"Budget",};}// [Expressive] members + ?. syntax — all translated to SQLvarresults=db.Orders.AsExpressiveDbSet().Where(o =>o.Customer?.Email!=null).Select(o =>new{o.Id,o.Total,Email=o.Customer?.Email,Grade=o.GetGrade()}).ToList();

Generated SQL (SQLite — other providers produce equivalent dialect-specific SQL):

SELECT"o"."Id",
"o"."Price"* CAST("o"."Quantity"ASREAL) AS"Total",
"c"."Email",
CASE
WHEN "o"."Price">=100.0 THEN 'Premium'
WHEN "o"."Price">=50.0 THEN 'Standard'
ELSE 'Budget'
END AS"Grade"FROM"Orders"AS"o"LEFT JOIN"Customers"AS"c"ON"o"."CustomerId"="c"."Id"WHERE"c"."Email"IS NOT NULL

Quick Start

dotnet add package ExpressiveSharp
# Lightweight alternative (attributes + source generator only, no runtime services):# dotnet add package ExpressiveSharp.Abstractions# Optional: EF Core integration
dotnet add package ExpressiveSharp.EntityFrameworkCore

See the BasicSample and EFCoreSample projects for runnable examples.

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>
Any IQueryable — modern syntax + [Expressive] expansion.AsExpressive()
EF Core — SQL window functions (ROW_NUMBER, RANK, etc.)WindowFunction.*
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]

Features

CategoryExamples
Null-conditional operators?. member access and indexer
Switch expressionsWith relational, logical, type, and property patterns
Pattern matchingConstant, type, relational, logical, property, positional, list patterns
String interpolationConverted to string.Concat
Constructor projections[Expressive] constructors for DTO projections with inheritance support
Block bodies (opt-in)if/else, switch statements, foreach/for loops, local variables
External member mapping[ExpressiveFor] for BCL/third-party members
Tuples, index/range, with, collection expressionsAnd more modern C# syntax
Expression transformersBuilt-in + custom IExpressionTreeTransformer pipeline
SQL window functionsROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST, SUM/AVG/COUNT/MIN/MAX OVER, LAG/LEAD, FIRST_VALUE/LAST_VALUE/NTH_VALUE with ROWS/RANGE frames
Hot reloadCompatible with dotnet watch — edits to [Expressive] bodies propagate to generated expression trees

See the full documentation for detailed usage, reference, and recipes.

Contributing

dotnet build # Build all projects
dotnet test# Run all tests

License

MIT

About

Source generator that enables modern C# syntax in LINQ expression trees

Topics

Resources

Stars

31 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages