ExpressiveSharp 0.2.1
dotnet add package ExpressiveSharp --version 0.2.1
NuGet\Install-Package ExpressiveSharp -Version 0.2.1
<PackageReference Include="ExpressiveSharp" Version="0.2.1" />
<PackageVersion Include="ExpressiveSharp" Version="0.2.1" />
<PackageReference Include="ExpressiveSharp" />
paket add ExpressiveSharp --version 0.2.1
#r "nuget: ExpressiveSharp, 0.2.1"
#:package ExpressiveSharp@0.2.1
#addin nuget:?package=ExpressiveSharp&version=0.2.1
#tool nuget:?package=ExpressiveSharp&version=0.2.1
ExpressiveSharp
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:
public class Order
{
public int Id { get; set; }
public double Price { get; set; }
public int Quantity { get; set; }
public Customer? Customer { get; set; }
// Computed property — reusable in any query, translated to SQL
[Expressive]
public double Total => Price * Quantity;
// Switch expression — normally illegal in expression trees
[Expressive]
public string GetGrade() => Price switch
{
>= 100 => "Premium",
>= 50 => "Standard",
_ => "Budget",
};
}
// [Expressive] members + ?. syntax — all translated to SQL
var results = 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" AS REAL) 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
# 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:
| Scenario | API |
|---|---|
EF Core — modern syntax + [Expressive] expansion on DbSet |
ExpressiveDbSet<T> |
Any IQueryable — modern syntax + [Expressive] expansion |
.WithExpressionRewrite() |
| EF Core — SQL window functions (ROW_NUMBER, RANK, etc.) | WindowFunction.* |
Advanced — build an Expression<T> inline, no attribute needed |
ExpressionPolyfill.Create |
Advanced — expand [Expressive] members in an existing expression tree |
.ExpandExpressives() |
| Advanced — make third-party/BCL members expressable | [ExpressiveFor] |
Features
| Category | Examples |
|---|---|
| Null-conditional operators | ?. member access and indexer |
| Switch expressions | With relational, logical, type, and property patterns |
| Pattern matching | Constant, type, relational, logical, property, positional, list patterns |
| String interpolation | Converted 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 expressions |
And more modern C# syntax |
| Expression transformers | Built-in + custom IExpressionTreeTransformer pipeline |
| SQL window functions | ROW_NUMBER, RANK, DENSE_RANK, NTILE (experimental) |
See the full documentation for detailed usage, reference, and recipes.
Contributing
dotnet build # Build all projects
dotnet test # Run all tests
- Testing Strategy — snapshot tests, functional tests, and test consumers
- How It Works — source generator internals
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on ExpressiveSharp:
| Package | Downloads |
|---|---|
|
ExpressiveSharp.EntityFrameworkCore
EF Core integration for ExpressiveSharp — automatically expands [Expressive] members in queries |
|
|
ExpressiveSharp.EntityFrameworkCore.RelationalExtensions
Experimental relational database extensions for ExpressiveSharp EF Core — window functions (ROW_NUMBER, RANK, DENSE_RANK, NTILE) and indexed Select. Note: EF Core has an open issue (dotnet/efcore#12747) for native window function support; this package may be superseded when that ships. |
GitHub repositories
This package is not used by any popular GitHub repositories.