Just Add [Expressive]
Decorate any property, method, or constructor with [Expressive] and the source generator does the rest β no boilerplate, no manual expression trees.
Write null-conditional operators, switch expressions, and pattern matching in your queries β source-generated at compile time with zero runtime overhead. Works with EF Core, MongoDB, and any IQueryable provider.
Decorate any property, method, or constructor with [Expressive] and the source generator does the rest β no boilerplate, no manual expression trees.
Null-conditional ?., switch expressions, pattern matching, string interpolation, tuples, list patterns, and more β all valid inside expression trees.
Works with EF Core (every provider β SQL Server, Postgres, SQLite, MySQL, Oracle, β¦), MongoDB, and any IQueryable. One library, every backend.
All expression trees are generated at compile time using Roslyn source generators. No reflection, no Compile(), no expression tree parsing at runtime.
[Expressive] members can call other [Expressive] members. Build a library of reusable query fragments and compose them freely in any query.
IExpressiveQueryable
Mark a constructor with [Expressive] to project DTOs directly in queries β new OrderDto(o) translates to a full provider projection.
Ranking (ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST), aggregate (SUM, AVG, COUNT, MIN, MAX), and navigation (LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE) functions with a fluent PARTITION BY / ORDER BY / frame API β via the RelationalExtensions package.
Built-in transformers adapt expression trees for your provider, plus plugin-contributed transformers. Implement IExpressionTreeTransformer for custom rewrites.
EXP0001βEXP0036 diagnostics catch projection errors at compile time. Quick-fix actions and migration fixers from Projectables included.
Without ExpressiveSharp β you hit two walls immediately:
// Problem 1: Computed properties are opaque to LINQ providers
public class Order
{
public decimal Price { get; set; }
public int Quantity { get; set; }
// The provider can't see inside this β it throws or silently fetches everything
public decimal Total => Price * Quantity;
}
// Problem 2: Modern C# syntax is illegal in expression trees
db.Orders
.Where(o => o.Customer?.Email != null) // CS8072: ?. not allowed
.Select(o => new { Grade = o.Price switch { // CS8514: switch not allowed
>= 100 => "Premium",
_ => "Budget"
}});You end up duplicating formulas as inline expressions and writing ugly ternary chains.
With ExpressiveSharp β write natural C#. The source generator handles it, and your provider (EF Core / MongoDB / your own IQueryable) gets a clean, translatable expression tree. Every doc page's live samples render the same query for SQLite, Postgres, SQL Server, MongoDB, and the generator output side-by-side β so you see exactly how it translates for your stack.
db
.Orders
.Where(o => o.Customer.Email != null && o.Total() > 500m)
.Select(o => new { o.Id, Total = o.Total(), Grade = o.Grade(), Email = o.Customer.Email })
// Setup
public static class OrderExt
{
[Expressive]
public static decimal Total(this Order o) => o.Items.Sum(i => i.UnitPrice * i.Quantity);
[Expressive]
public static string Grade(this Order o) => o.Total() switch
{
>= 1000m => "Premium",
>= 100m => "Standard",
_ => "Budget",
};
}SELECT "o"."Id", (
SELECT COALESCE(ef_sum(ef_multiply("l0"."UnitPrice", CAST("l0"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l0"
WHERE "o"."Id" = "l0"."OrderId") AS "Total", CASE
WHEN ef_compare((
SELECT COALESCE(ef_sum(ef_multiply("l1"."UnitPrice", CAST("l1"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l1"
WHERE "o"."Id" = "l1"."OrderId"), '1000.0') >= 0 THEN 'Premium'
WHEN ef_compare((
SELECT COALESCE(ef_sum(ef_multiply("l2"."UnitPrice", CAST("l2"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l2"
WHERE "o"."Id" = "l2"."OrderId"), '100.0') >= 0 THEN 'Standard'
ELSE 'Budget'
END AS "Grade", "c"."Email"
FROM "Orders" AS "o"
INNER JOIN "Customers" AS "c" ON "o"."CustomerId" = "c"."Id"
WHERE "c"."Email" IS NOT NULL AND ef_compare((
SELECT COALESCE(ef_sum(ef_multiply("l"."UnitPrice", CAST("l"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId"), '500.0') > 0SELECT o."Id", (
SELECT COALESCE(sum(l0."UnitPrice" * l0."Quantity"::numeric(18,2)), 0.0)
FROM "LineItems" AS l0
WHERE o."Id" = l0."OrderId") AS "Total", CASE
WHEN (
SELECT COALESCE(sum(l1."UnitPrice" * l1."Quantity"::numeric(18,2)), 0.0)
FROM "LineItems" AS l1
WHERE o."Id" = l1."OrderId") >= 1000.0 THEN 'Premium'
WHEN (
SELECT COALESCE(sum(l2."UnitPrice" * l2."Quantity"::numeric(18,2)), 0.0)
FROM "LineItems" AS l2
WHERE o."Id" = l2."OrderId") >= 100.0 THEN 'Standard'
ELSE 'Budget'
END AS "Grade", c."Email"
FROM "Orders" AS o
INNER JOIN "Customers" AS c ON o."CustomerId" = c."Id"
WHERE c."Email" IS NOT NULL AND (
SELECT COALESCE(sum(l."UnitPrice" * l."Quantity"::numeric(18,2)), 0.0)
FROM "LineItems" AS l
WHERE o."Id" = l."OrderId") > 500.0SELECT [o].[Id], (
SELECT COALESCE(SUM([l0].[UnitPrice] * CAST([l0].[Quantity] AS decimal(18,2))), 0.0)
FROM [LineItems] AS [l0]
WHERE [o].[Id] = [l0].[OrderId]) AS [Total], CASE
WHEN (
SELECT COALESCE(SUM([l1].[UnitPrice] * CAST([l1].[Quantity] AS decimal(18,2))), 0.0)
FROM [LineItems] AS [l1]
WHERE [o].[Id] = [l1].[OrderId]) >= 1000.0 THEN N'Premium'
WHEN (
SELECT COALESCE(SUM([l2].[UnitPrice] * CAST([l2].[Quantity] AS decimal(18,2))), 0.0)
FROM [LineItems] AS [l2]
WHERE [o].[Id] = [l2].[OrderId]) >= 100.0 THEN N'Standard'
ELSE N'Budget'
END AS [Grade], [c].[Email]
FROM [Orders] AS [o]
INNER JOIN [Customers] AS [c] ON [o].[CustomerId] = [c].[Id]
WHERE [c].[Email] IS NOT NULL AND (
SELECT COALESCE(SUM([l].[UnitPrice] * CAST([l].[Quantity] AS decimal(18,2))), 0.0)
FROM [LineItems] AS [l]
WHERE [o].[Id] = [l].[OrderId]) > 500.0playground.orders.Aggregate([
{
"$match" : {
"$and" : [
{
"Customer.Email" : { "$ne" : null }
},
{
"$expr" : {
"$gt" : [
{
"$sum" : {
"$map" : {
"input" : "$Items",
"as" : "i",
"in" : {
"$multiply" : ["$$i.UnitPrice", "$$i.Quantity"]
}
}
}
},
{ "$numberDecimal" : "500" }
]
}
}
]
}
},
{
"$project" : {
"_id" : "$_id",
"Total" : {
"$sum" : {
"$map" : {
"input" : "$Items",
"as" : "i",
"in" : {
"$multiply" : ["$$i.UnitPrice", "$$i.Quantity"]
}
}
}
},
"Grade" : {
"$cond" : {
"if" : {
"$gte" : [
{
"$sum" : {
"$map" : {
"input" : "$Items",
"as" : "i",
"in" : {
"$multiply" : ["$$i.UnitPrice", "$$i.Quantity"]
}
}
}
},
{ "$numberDecimal" : "1000" }
]
},
"then" : "Premium",
"else" : {
"$cond" : {
"if" : {
"$gte" : [
{
"$sum" : {
"$map" : {
"input" : "$Items",
"as" : "i",
"in" : {
"$multiply" : ["$$i.UnitPrice", "$$i.Quantity"]
}
}
}
},
{ "$numberDecimal" : "100" }
]
},
"then" : "Standard",
"else" : "Budget"
}
}
}
},
"Email" : "$Customer.Email"
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_OrderExt.Total_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order.g.cs ===
// <auto-generated/>
#nullable disable
using System;
using System.Linq;
using System.Linq.Expressions;
using ExpressiveSharp;
using ExpressiveSharp.EntityFrameworkCore;
using ExpressiveSharp.Docs.PlaygroundModel.Webshop;
using ExpressiveSharp.Docs.Playground.Snippet;
namespace ExpressiveSharp.Generated
{
static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderExt
{
// [Expressive]
// public static decimal Total(this Order o) => o.Items.Sum(i => i.UnitPrice * i.Quantity);
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, decimal>> Total_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression()
{
var p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
var expr_1 = global::System.Linq.Expressions.Expression.Property(p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("Items", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Items
var p_i_2 = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem), "i"); // i => i.UnitPrice * i.Quantity
var expr_4 = global::System.Linq.Expressions.Expression.Property(p_i_2, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem).GetProperty("UnitPrice", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // i.UnitPrice
var expr_6 = global::System.Linq.Expressions.Expression.Property(p_i_2, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem).GetProperty("Quantity", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // i.Quantity
var expr_5 = global::System.Linq.Expressions.Expression.Convert(expr_6, typeof(decimal));
var expr_3 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_4, expr_5);
var expr_7 = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem, decimal>>(expr_3, p_i_2);
var expr_0 = global::System.Linq.Expressions.Expression.Call(global::System.Linq.Enumerable.First(global::System.Linq.Enumerable.Where(typeof(global::System.Linq.Enumerable).GetMethods(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static), m => m.Name == "Sum" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType.IsGenericType && !m.GetParameters()[0].ParameterType.IsGenericParameter && m.GetParameters()[1].ParameterType.IsGenericType && !m.GetParameters()[1].ParameterType.IsGenericParameter && m.GetParameters()[1].ParameterType.GetGenericArguments()[1] == typeof(decimal))).MakeGenericMethod(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem)), new global::System.Linq.Expressions.Expression[] { expr_1, expr_7 });
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, decimal>>(expr_0, p_o);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderExt.Grade_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order.g.cs ===
// <auto-generated/>
#nullable disable
using System;
using System.Linq;
using System.Linq.Expressions;
using ExpressiveSharp;
using ExpressiveSharp.EntityFrameworkCore;
using ExpressiveSharp.Docs.PlaygroundModel.Webshop;
using ExpressiveSharp.Docs.Playground.Snippet;
namespace ExpressiveSharp.Generated
{
static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderExt
{
// [Expressive]
// public static string Grade(this Order o) => o.Total() switch
// {
// >= 1000m => "Premium",
// >= 100m => "Standard",
// _ => "Budget",
// };
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>> Grade_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression()
{
var p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
var expr_0 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderExt).GetMethod("Total", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), new global::System.Linq.Expressions.Expression[] { p_o }); // o.Total()
var expr_1 = global::System.Linq.Expressions.Expression.Constant("Budget", typeof(string)); // "Budget"
var expr_3 = global::System.Linq.Expressions.Expression.Constant(100m, typeof(decimal)); // 100m
var expr_2 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_0, expr_3);
var expr_4 = global::System.Linq.Expressions.Expression.Constant("Standard", typeof(string)); // "Standard"
var expr_5 = global::System.Linq.Expressions.Expression.Condition(expr_2, expr_4, expr_1, typeof(string));
var expr_7 = global::System.Linq.Expressions.Expression.Constant(1000m, typeof(decimal)); // 1000m
var expr_6 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_0, expr_7);
var expr_8 = global::System.Linq.Expressions.Expression.Constant("Premium", typeof(string)); // "Premium"
var expr_9 = global::System.Linq.Expressions.Expression.Condition(expr_6, expr_8, expr_5, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(expr_9, p_o);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderExt.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderExt { }
}
// === ExpressionRegistry.g.cs ===
// <auto-generated/>
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
internal static class ExpressionRegistry
{
private static Dictionary<nint, LambdaExpression> Build()
{
const BindingFlags allFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
var map = new Dictionary<nint, LambdaExpression>();
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderExt).GetMethod("Total", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderExt", "Total_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression");
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderExt).GetMethod("Grade", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderExt", "Grade_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression");
return map;
}
private static volatile Dictionary<nint, LambdaExpression> _map = Build();
internal static void ResetMap() => _map = Build();
public static LambdaExpression TryGet(MemberInfo member)
{
var handle = member switch
{
MethodInfo m => (nint?)m.MethodHandle.Value,
PropertyInfo p => p.GetMethod?.MethodHandle.Value,
ConstructorInfo c => (nint?)c.MethodHandle.Value,
_ => null
};
return handle.HasValue && _map.TryGetValue(handle.Value, out var expr) ? expr : null;
}
private static void Register(Dictionary<nint, LambdaExpression> map, MethodBase m, string exprClass, string exprMethodName)
{
if (m is null) return;
var exprType = m.DeclaringType?.Assembly.GetType(exprClass) ?? typeof(ExpressionRegistry).Assembly.GetType(exprClass);
var exprMethod = exprType?.GetMethod(exprMethodName, BindingFlags.Static | BindingFlags.NonPublic);
if (exprMethod is null) return;
var expr = (LambdaExpression)exprMethod.Invoke(null, null)!;
// Apply declared transformers from the generated class (if any)
const string expressionSuffix = "_Expression";
if (exprMethodName.EndsWith(expressionSuffix, StringComparison.Ordinal))
{
var transformersSuffix = exprMethodName.Substring(0, exprMethodName.Length - expressionSuffix.Length) + "_Transformers";
var transformersMethod = exprType.GetMethod(transformersSuffix, BindingFlags.Static | BindingFlags.NonPublic);
if (transformersMethod?.Invoke(null, null) is global::ExpressiveSharp.IExpressionTreeTransformer[] transformers)
{
Expression transformed = expr;
foreach (var t in transformers) transformed = t.Transform(transformed);
if (transformed is LambdaExpression lambdaResult) expr = lambdaResult;
}
}
map[m.MethodHandle.Value] = expr;
}
}
}
// === PolyfillInterceptors_b1293e61.g.cs ===
// <auto-generated/>
#nullable disable
namespace ExpressiveSharp.Generated.Interceptors
{
internal static partial class PolyfillInterceptors
{
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "hE6p2/dyADJlwhe23gr9/cIBAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<T1> __Polyfill_Select_3e61_16_5<T0, T1>(
this global::ExpressiveSharp.IExpressiveQueryable<T0> source,
global::System.Func<T0, T1> __func)
{
// Source: o => new { o.Id, Total = o.Total(), Grade = o.Grade(), Email = o.Customer.Email }
var i3e6116c5_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(T0), "o");
var i3e6116c5_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6116c5_p_o, typeof(T0).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Id
var i3e6116c5_expr_2 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderExt).GetMethod("Total", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(T0) }, null), new global::System.Linq.Expressions.Expression[] { i3e6116c5_p_o }); // o.Total()
var i3e6116c5_expr_3 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderExt).GetMethod("Grade", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(T0) }, null), new global::System.Linq.Expressions.Expression[] { i3e6116c5_p_o }); // o.Grade()
var i3e6116c5_expr_5 = global::System.Linq.Expressions.Expression.Property(i3e6116c5_p_o, typeof(T0).GetProperty("Customer", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Customer
var i3e6116c5_expr_4 = global::System.Linq.Expressions.Expression.Property(i3e6116c5_expr_5, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Email", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
var i3e6116c5_expr_6 = typeof(T1).GetConstructors()[0];
var i3e6116c5_expr_0 = global::System.Linq.Expressions.Expression.New(i3e6116c5_expr_6, new global::System.Linq.Expressions.Expression[] { i3e6116c5_expr_1, i3e6116c5_expr_2, i3e6116c5_expr_3, i3e6116c5_expr_4 }, new global::System.Reflection.MemberInfo[] { typeof(T1).GetProperty("Id"), typeof(T1).GetProperty("Total"), typeof(T1).GetProperty("Grade"), typeof(T1).GetProperty("Email") });
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<T0, T1>>(i3e6116c5_expr_0, i3e6116c5_p_o);
return (global::ExpressiveSharp.IExpressiveQueryable<T1>)(object)
global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<T0>)(object)source,
__lambda));
}
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "hE6p2/dyADJlwhe23gr9/YQBAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> __Polyfill_Where_3e61_15_5(
this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, bool> __func)
{
// Source: o => o.Customer.Email != null && o.Total() > 500m
var i3e6115c5_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
var i3e6115c5_expr_3 = global::System.Linq.Expressions.Expression.Property(i3e6115c5_p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("Customer", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Customer
var i3e6115c5_expr_2 = global::System.Linq.Expressions.Expression.Property(i3e6115c5_expr_3, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Email", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
var i3e6115c5_expr_5 = global::System.Linq.Expressions.Expression.Constant(null, typeof(object)); // null
var i3e6115c5_expr_4 = global::System.Linq.Expressions.Expression.Convert(i3e6115c5_expr_5, typeof(string));
var i3e6115c5_expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.NotEqual, i3e6115c5_expr_2, i3e6115c5_expr_4);
var i3e6115c5_expr_7 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderExt).GetMethod("Total", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), new global::System.Linq.Expressions.Expression[] { i3e6115c5_p_o }); // o.Total()
var i3e6115c5_expr_8 = global::System.Linq.Expressions.Expression.Constant(500m, typeof(decimal)); // 500m
var i3e6115c5_expr_6 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, i3e6115c5_expr_7, i3e6115c5_expr_8);
var i3e6115c5_expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.AndAlso, i3e6115c5_expr_1, i3e6115c5_expr_6);
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, bool>>(i3e6115c5_expr_0, i3e6115c5_p_o);
return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Where(
(global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order>)source,
__lambda));
}
}
}
namespace System.Runtime.CompilerServices
{
[global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : global::System.Attribute
{
public InterceptsLocationAttribute(int version, string data) { }
}
}db
.Orders
.Where(o => o.Customer.Email != null && o.Total() > 500m)
.Select(o => new { o.Id, Total = o.Total(), Grade = o.Grade(), Email = o.Customer.Email })
// Setup
public static class OrderExt
{
[Expressive]
public static decimal Total(this Order o) => o.Items.Sum(i => i.UnitPrice * i.Quantity);
[Expressive]
public static string Grade(this Order o) => o.Total() switch
{
>= 1000m => "Premium",
>= 100m => "Standard",
_ => "Budget",
};
}Generated SQL:
SELECT "o"."Id", (
SELECT COALESCE(ef_sum(ef_multiply("l0"."UnitPrice", CAST("l0"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l0"
WHERE "o"."Id" = "l0"."OrderId") AS "Total", CASE
WHEN ef_compare((
SELECT COALESCE(ef_sum(ef_multiply("l1"."UnitPrice", CAST("l1"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l1"
WHERE "o"."Id" = "l1"."OrderId"), '1000.0') >= 0 THEN 'Premium'
WHEN ef_compare((
SELECT COALESCE(ef_sum(ef_multiply("l2"."UnitPrice", CAST("l2"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l2"
WHERE "o"."Id" = "l2"."OrderId"), '100.0') >= 0 THEN 'Standard'
ELSE 'Budget'
END AS "Grade", "c"."Email"
FROM "Orders" AS "o"
INNER JOIN "Customers" AS "c" ON "o"."CustomerId" = "c"."Id"
WHERE "c"."Email" IS NOT NULL AND ef_compare((
SELECT COALESCE(ef_sum(ef_multiply("l"."UnitPrice", CAST("l"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId"), '500.0') > 0Computed properties are inlined into the provider's native query language β no client-side evaluation, no N+1. Modern syntax just works.
| Package | Description |
|---|---|
ExpressiveSharp | Core runtime β expression expansion, transformers, IExpressiveQueryable<T>, ExpressionPolyfill |
ExpressiveSharp.Abstractions | Lightweight β attributes ([Expressive], [ExpressiveFor]), IExpressionTreeTransformer, source generator only |
ExpressiveSharp.EntityFrameworkCore | EF Core integration β UseExpressives(), ExpressiveDbSet<T>, Include/ThenInclude, async methods |
ExpressiveSharp.MongoDB | MongoDB integration β .AsExpressive() on IMongoCollection<T>, MQL translation |
ExpressiveSharp.EntityFrameworkCore.RelationalExtensions | SQL window functions β ranking, aggregate, navigation with ROWS/RANGE frames |
