[Expressive] Attribute
The ExpressiveAttribute is the primary entry point for ExpressiveSharp. Place it on any property, method, extension method, or constructor to tell the source generator to produce a companion expression tree at compile time.
Namespace
using ExpressiveSharp;Targets
| Target | Supported |
|---|---|
| Properties | Yes |
| Methods | Yes |
| Extension methods | Yes |
| Constructors | Yes |
| Indexers | No |
The attribute can be inherited by derived types (Inherited = true).
Properties
AllowBlockBody
Type: boolDefault: false
Enables block-bodied member support. Without this flag, using a block body ({ }) with [Expressive] produces error EXP0004. Setting this to true allows block bodies that support local variables, if/else, switch statements, and foreach loops.
When not explicitly set on the attribute, the MSBuild property Expressive_AllowBlockBody is used as the global default (also defaults to false).
db
.Orders
.Select(o => o.GetCategory())
// Setup
public static class OrderBlockExt
{
[Expressive(AllowBlockBody = true)]
public static string GetCategory(this Order o)
{
var threshold = o.Items.Count() * 10;
if (threshold > 100) return "Bulk";
return "Regular";
}
}SELECT CASE
WHEN (
SELECT COUNT(*)
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId") * 10 > 100 THEN 'Bulk'
ELSE 'Regular'
END
FROM "Orders" AS "o"SELECT CASE
WHEN (
SELECT count(*)::int
FROM "LineItems" AS l
WHERE o."Id" = l."OrderId") * 10 > 100 THEN 'Bulk'
ELSE 'Regular'
END
FROM "Orders" AS oSELECT CASE
WHEN (
SELECT COUNT(*)
FROM [LineItems] AS [l]
WHERE [o].[Id] = [l].[OrderId]) * 10 > 100 THEN N'Bulk'
ELSE N'Regular'
END
FROM [Orders] AS [o]playground.orders.Aggregate([
{
"$project" : {
"_v" : {
"$cond" : {
"if" : {
"$gt" : [
{
"$multiply" : [
{ "$size" : "$Items" },
10
]
},
100
]
},
"then" : "Bulk",
"else" : "Regular"
}
},
"_id" : 0
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_OrderBlockExt.GetCategory_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_OrderBlockExt
{
// [Expressive(AllowBlockBody = true)]
// public static string GetCategory(this Order o)
// {
// var threshold = o.Items.Count() * 10;
// if (threshold > 100)
// return "Bulk";
// return "Regular";
// }
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>> GetCategory_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.Variable(typeof(int), "threshold"); // { var threshold = o.Items.Count() * 10; i...
var expr_3 = 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 expr_2 = 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 == "Count" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && !m.GetParameters()[0].ParameterType.IsGenericParameter)).MakeGenericMethod(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem)), new global::System.Linq.Expressions.Expression[] { expr_3 });
var expr_4 = global::System.Linq.Expressions.Expression.Constant(10, typeof(int)); // 10
var expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_2, expr_4);
var expr_5 = global::System.Linq.Expressions.Expression.Assign(expr_0, expr_1);
var expr_8 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
var expr_7 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, expr_0, expr_8);
var expr_9 = global::System.Linq.Expressions.Expression.Constant("Bulk", typeof(string)); // "Bulk"
var expr_10 = global::System.Linq.Expressions.Expression.Constant("Regular", typeof(string)); // "Regular"
var expr_6 = global::System.Linq.Expressions.Expression.Condition(expr_7, expr_9, expr_10, typeof(string));
var expr_11 = global::System.Linq.Expressions.Expression.Block(new global::System.Linq.Expressions.ParameterExpression[] { expr_0 }, new global::System.Linq.Expressions.Expression[] { expr_5, expr_6 });
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(expr_11, p_o);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderBlockExt.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderBlockExt { }
}
// === 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.OrderBlockExt).GetMethod("GetCategory", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderBlockExt", "GetCategory_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, "42s8kkCcX7lCW64QyxxYuX8BAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<string> __Polyfill_Select_3e61_14_21(
this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string> __func)
{
// Source: o => o.GetCategory()
var i3e6114c21_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
var i3e6114c21_expr_0 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderBlockExt).GetMethod("GetCategory", 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[] { i3e6114c21_p_o }); // o.GetCategory()
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(i3e6114c21_expr_0, i3e6114c21_p_o);
return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(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
.Select(o => o.GetCategory())
// Setup
public static class OrderBlockExt
{
[Expressive(AllowBlockBody = true)]
public static string GetCategory(this Order o)
{
var threshold = o.Items.Count() * 10;
if (threshold > 100) return "Bulk";
return "Regular";
}
}Generated SQL:
SELECT CASE
WHEN (
SELECT COUNT(*)
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId") * 10 > 100 THEN 'Bulk'
ELSE 'Regular'
END
FROM "Orders" AS "o"Or enable globally for the entire project:
<PropertyGroup>
<Expressive_AllowBlockBody>true</Expressive_AllowBlockBody>
</PropertyGroup>Transformers
Type: Type[]?Default: null
Specifies additional IExpressionTreeTransformer types to apply at runtime when the expression is resolved. Each type must have a parameterless constructor.
db
.Orders
.Select(o => o.CustomerName())
// Setup
public static class OrderTransformerExt
{
[Expressive(Transformers = new[] { typeof(ExpressiveSharp.Transformers.RemoveNullConditionalPatterns) })]
public static string? CustomerName(this Order o) => o.Customer?.Name;
}SELECT "c"."Name"
FROM "Orders" AS "o"
INNER JOIN "Customers" AS "c" ON "o"."CustomerId" = "c"."Id"SELECT c."Name"
FROM "Orders" AS o
INNER JOIN "Customers" AS c ON o."CustomerId" = c."Id"SELECT [c].[Name]
FROM [Orders] AS [o]
INNER JOIN [Customers] AS [c] ON [o].[CustomerId] = [c].[Id]playground.orders.Aggregate([
{
"$project" : { "_v" : "$Customer.Name", "_id" : 0 }
}
])// === ExpressiveSharp_Docs_Playground_Snippet_OrderTransformerExt.CustomerName_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_OrderTransformerExt
{
// [Expressive(Transformers = new[] { typeof(ExpressiveSharp.Transformers.RemoveNullConditionalPatterns) })]
// public static string? CustomerName(this Order o) => o.Customer?.Name;
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>> CustomerName_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.Property(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 expr_1 = global::System.Linq.Expressions.Expression.Property(expr_0, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // .Name
var expr_3 = global::System.Linq.Expressions.Expression.Constant(null, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer));
var expr_4 = global::System.Linq.Expressions.Expression.NotEqual(expr_0, expr_3);
var expr_5 = global::System.Linq.Expressions.Expression.Default(typeof(string));
var expr_2 = global::System.Linq.Expressions.Expression.Condition(expr_4, expr_1, expr_5, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(expr_2, p_o);
}
static global::ExpressiveSharp.IExpressionTreeTransformer[] CustomerName_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Transformers() => [new global::ExpressiveSharp.Transformers.RemoveNullConditionalPatterns()];
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderTransformerExt.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderTransformerExt { }
}
// === 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.OrderTransformerExt).GetMethod("CustomerName", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderTransformerExt", "CustomerName_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, "rz/h1nNxWIEJavSBLYWEeH8BAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<string> __Polyfill_Select_3e61_14_21(
this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string> __func)
{
// Source: o => o.CustomerName()
var i3e6114c21_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
var i3e6114c21_expr_0 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderTransformerExt).GetMethod("CustomerName", 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[] { i3e6114c21_p_o }); // o.CustomerName()
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(i3e6114c21_expr_0, i3e6114c21_p_o);
return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(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
.Select(o => o.CustomerName())
// Setup
public static class OrderTransformerExt
{
[Expressive(Transformers = new[] { typeof(ExpressiveSharp.Transformers.RemoveNullConditionalPatterns) })]
public static string? CustomerName(this Order o) => o.Customer?.Name;
}Generated SQL:
SELECT "c"."Name"
FROM "Orders" AS "o"
INNER JOIN "Customers" AS "c" ON "o"."CustomerId" = "c"."Id"See Expression Transformers for the full list of built-in transformers and how to create custom ones.
How It Works
When the source generator encounters an [Expressive] member, it:
- Analyzes the member body at the IOperation (semantic) level
- Generates
Expression<Func<...>>factory code usingExpression.*calls - Registers the generated expression in a per-assembly expression registry
At runtime, ExpandExpressives() (or UseExpressives() in EF Core) looks up the registered expression and replaces opaque member accesses with the generated expression tree, so LINQ providers can translate them.
No NullConditionalRewriteSupport enum
Unlike Projectables, which required a per-member NullConditionalRewriteSupport enum to configure ?. handling, ExpressiveSharp always generates a faithful ternary (x != null ? x.Prop : default). If you need to strip the null checks for SQL providers, the RemoveNullConditionalPatterns transformer handles it globally. UseExpressives() applies this transformer automatically. See Null-Conditional Rewrite for details.
No ExpandEnumMethods property
ExpressiveSharp always expands enum extension methods into per-value ternary chains automatically. There is no opt-in flag needed.
No CompatibilityMode
ExpressiveSharp does not have a compatibility mode setting. Expression expansion always uses the full approach, which handles all scenarios correctly.
Using ExpandExpressives()
After marking members with [Expressive], you can manually expand them in expression trees using the .ExpandExpressives() extension method:
Expression<Func<Order, decimal>> expr = o => o.Total();
// expr body is: o.Total() (opaque method call)
var expanded = expr.ExpandExpressives();
// expanded body is: o.Items.Sum(i => i.UnitPrice * i.Quantity) (translatable by your provider)This replaces [Expressive] member references with their generated expression trees. Expansion is recursive -- if TotalWithTax references Total, both are expanded:
[Expressive]
public static decimal Total(this Order o) => o.Items.Sum(i => i.UnitPrice * i.Quantity);
[Expressive]
public static decimal TotalWithTax(this Order o) => o.Total() * 1.08m;
Expression<Func<Order, decimal>> expr = o => o.TotalWithTax();
var expanded = expr.ExpandExpressives();
// expanded body is: o.Items.Sum(i => i.UnitPrice * i.Quantity) * 1.08mYou can also pass transformers to ExpandExpressives():
expr.ExpandExpressives(new RemoveNullConditionalPatterns());Or register transformers globally so all calls use them:
ExpressiveOptions.Default.AddTransformers(new RemoveNullConditionalPatterns());
expr.ExpandExpressives(); // RemoveNullConditionalPatterns applied automaticallyOpting Out: [NotExpressive]
Use [NotExpressive] to mark a member that looks expressive-eligible (it has an expression body that the source generator could lift) but should intentionally remain runtime-evaluated. The attribute suppresses the analyzer suggestions:
- EXP0013 — "Member could benefit from
[Expressive]" - EXP0027 — "Plain
IQueryablechain references an[Expressive]member without.AsExpressive()"
public class Order
{
public Guid Id { get; set; }
// Always evaluated in-memory — captures process-local state that would not
// survive translation. Suppress the "could be [Expressive]" suggestion.
[NotExpressive]
public string DebugLabel => $"{Id} (pid {System.Environment.ProcessId})";
}[NotExpressive] cannot be combined with [Expressive] on the same member.
Complete Example
db
.Orders
.Where(o => o.CustomerEmail() != null)
.Select(o => new OrderSummaryDto(o.Id, o.SafeTag(), o.Total()))
// Setup
public static class OrderComplete
{
// Simple computed method
[Expressive]
public static decimal Total(this Order o) => o.Items.Sum(i => i.UnitPrice * i.Quantity);
// Composing expressives
[Expressive]
public static decimal TotalWithTax(this Order o) => o.Total() * 1.08m;
// Null-conditional operators -- always generates faithful ternary
[Expressive]
public static string? CustomerEmail(this Order o) => o.Customer?.Email;
// Switch expressions with pattern matching
[Expressive]
public static string GetGrade(this Order o) => o.Items.Count() switch
{
>= 10 => "Premium",
>= 5 => "Standard",
_ => "Budget",
};
// Per-member transformer
[Expressive(Transformers = new[] { typeof(ExpressiveSharp.Transformers.RemoveNullConditionalPatterns) })]
public static string? CustomerNameSafe(this Order o) => o.Customer?.Name;
// Block body (opt-in)
[Expressive(AllowBlockBody = true)]
public static string GetCategory(this Order o)
{
var threshold = o.Items.Count() * 10;
if (threshold > 100) return "Bulk";
return "Regular";
}
// Extension method with null-coalescing
[Expressive]
public static string SafeTag(this Order o) => o.Customer != null ? o.Customer.Name : "N/A";
}
public class OrderSummaryDto
{
public int Id { get; set; }
public string Description { get; set; } = "";
public decimal Total { get; set; }
public OrderSummaryDto() { }
// Constructor projection -- translates to MemberInit
[Expressive]
public OrderSummaryDto(int id, string description, decimal total)
{
Id = id;
Description = description;
Total = total;
}
}SELECT "o"."Id", "c"."Name" AS "Description", (
SELECT COALESCE(ef_sum(ef_multiply("l"."UnitPrice", CAST("l"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId") AS "Total"
FROM "Orders" AS "o"
INNER JOIN "Customers" AS "c" ON "o"."CustomerId" = "c"."Id"
WHERE "c"."Email" IS NOT NULLSELECT o."Id", c."Name" AS "Description", (
SELECT COALESCE(sum(l."UnitPrice" * l."Quantity"::numeric(18,2)), 0.0)
FROM "LineItems" AS l
WHERE o."Id" = l."OrderId") AS "Total"
FROM "Orders" AS o
INNER JOIN "Customers" AS c ON o."CustomerId" = c."Id"
WHERE c."Email" IS NOT NULLSELECT [o].[Id], [c].[Name] AS [Description], (
SELECT COALESCE(SUM([l].[UnitPrice] * CAST([l].[Quantity] AS decimal(18,2))), 0.0)
FROM [LineItems] AS [l]
WHERE [o].[Id] = [l].[OrderId]) AS [Total]
FROM [Orders] AS [o]
INNER JOIN [Customers] AS [c] ON [o].[CustomerId] = [c].[Id]
WHERE [c].[Email] IS NOT NULLplayground.orders.Aggregate([
{
"$match" : {
"Customer.Email" : { "$ne" : null }
}
},
{
"$project" : {
"_id" : "$_id",
"Description" : {
"$cond" : {
"if" : {
"$ne" : ["$Customer", null]
},
"then" : "$Customer.Name",
"else" : "N/A"
}
},
"Total" : {
"$sum" : {
"$map" : {
"input" : "$Items",
"as" : "i",
"in" : {
"$multiply" : ["$$i.UnitPrice", "$$i.Quantity"]
}
}
}
}
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_OrderComplete.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_OrderComplete
{
// // Simple computed method
// [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_OrderComplete.TotalWithTax_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_OrderComplete
{
// // Composing expressives
// [Expressive]
// public static decimal TotalWithTax(this Order o) => o.Total() * 1.08m;
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, decimal>> TotalWithTax_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.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderComplete).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_2 = global::System.Linq.Expressions.Expression.Constant(1.08m, typeof(decimal)); // 1.08m
var expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_1, expr_2);
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_OrderComplete.CustomerEmail_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_OrderComplete
{
// // Null-conditional operators -- always generates faithful ternary
// [Expressive]
// public static string? CustomerEmail(this Order o) => o.Customer?.Email;
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>> CustomerEmail_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.Property(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 expr_1 = global::System.Linq.Expressions.Expression.Property(expr_0, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Email", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // .Email
var expr_3 = global::System.Linq.Expressions.Expression.Constant(null, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer));
var expr_4 = global::System.Linq.Expressions.Expression.NotEqual(expr_0, expr_3);
var expr_5 = global::System.Linq.Expressions.Expression.Default(typeof(string));
var expr_2 = global::System.Linq.Expressions.Expression.Condition(expr_4, expr_1, expr_5, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(expr_2, p_o);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderComplete.GetGrade_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_OrderComplete
{
// // Switch expressions with pattern matching
// [Expressive]
// public static string GetGrade(this Order o) => o.Items.Count() switch
// {
// >= 10 => "Premium",
// >= 5 => "Standard",
// _ => "Budget",
// };
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>> GetGrade_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 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 == "Count" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && !m.GetParameters()[0].ParameterType.IsGenericParameter)).MakeGenericMethod(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem)), new global::System.Linq.Expressions.Expression[] { expr_1 });
var expr_2 = global::System.Linq.Expressions.Expression.Constant("Budget", typeof(string)); // "Budget"
var expr_4 = global::System.Linq.Expressions.Expression.Constant(5, typeof(int)); // 5
var expr_3 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_0, expr_4);
var expr_5 = global::System.Linq.Expressions.Expression.Constant("Standard", typeof(string)); // "Standard"
var expr_6 = global::System.Linq.Expressions.Expression.Condition(expr_3, expr_5, expr_2, typeof(string));
var expr_8 = global::System.Linq.Expressions.Expression.Constant(10, typeof(int)); // 10
var expr_7 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_0, expr_8);
var expr_9 = global::System.Linq.Expressions.Expression.Constant("Premium", typeof(string)); // "Premium"
var expr_10 = global::System.Linq.Expressions.Expression.Condition(expr_7, expr_9, expr_6, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(expr_10, p_o);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderComplete.CustomerNameSafe_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_OrderComplete
{
// // Per-member transformer
// [Expressive(Transformers = new[] { typeof(ExpressiveSharp.Transformers.RemoveNullConditionalPatterns) })]
// public static string? CustomerNameSafe(this Order o) => o.Customer?.Name;
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>> CustomerNameSafe_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.Property(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 expr_1 = global::System.Linq.Expressions.Expression.Property(expr_0, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // .Name
var expr_3 = global::System.Linq.Expressions.Expression.Constant(null, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer));
var expr_4 = global::System.Linq.Expressions.Expression.NotEqual(expr_0, expr_3);
var expr_5 = global::System.Linq.Expressions.Expression.Default(typeof(string));
var expr_2 = global::System.Linq.Expressions.Expression.Condition(expr_4, expr_1, expr_5, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(expr_2, p_o);
}
static global::ExpressiveSharp.IExpressionTreeTransformer[] CustomerNameSafe_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Transformers() => [new global::ExpressiveSharp.Transformers.RemoveNullConditionalPatterns()];
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderComplete.GetCategory_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_OrderComplete
{
// // Block body (opt-in)
// [Expressive(AllowBlockBody = true)]
// public static string GetCategory(this Order o)
// {
// var threshold = o.Items.Count() * 10;
// if (threshold > 100)
// return "Bulk";
// return "Regular";
// }
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>> GetCategory_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.Variable(typeof(int), "threshold"); // { var threshold = o.Items.Count() * 10; i...
var expr_3 = 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 expr_2 = 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 == "Count" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && !m.GetParameters()[0].ParameterType.IsGenericParameter)).MakeGenericMethod(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem)), new global::System.Linq.Expressions.Expression[] { expr_3 });
var expr_4 = global::System.Linq.Expressions.Expression.Constant(10, typeof(int)); // 10
var expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_2, expr_4);
var expr_5 = global::System.Linq.Expressions.Expression.Assign(expr_0, expr_1);
var expr_8 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
var expr_7 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, expr_0, expr_8);
var expr_9 = global::System.Linq.Expressions.Expression.Constant("Bulk", typeof(string)); // "Bulk"
var expr_10 = global::System.Linq.Expressions.Expression.Constant("Regular", typeof(string)); // "Regular"
var expr_6 = global::System.Linq.Expressions.Expression.Condition(expr_7, expr_9, expr_10, typeof(string));
var expr_11 = global::System.Linq.Expressions.Expression.Block(new global::System.Linq.Expressions.ParameterExpression[] { expr_0 }, new global::System.Linq.Expressions.Expression[] { expr_5, expr_6 });
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(expr_11, p_o);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderComplete.SafeTag_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_OrderComplete
{
// // Extension method with null-coalescing
// [Expressive]
// public static string SafeTag(this Order o) => o.Customer != null ? o.Customer.Name : "N/A";
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>> SafeTag_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_3 = global::System.Linq.Expressions.Expression.Property(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 expr_2 = global::System.Linq.Expressions.Expression.Convert(expr_3, typeof(object));
var expr_5 = global::System.Linq.Expressions.Expression.Constant(null, typeof(object)); // null
var expr_4 = global::System.Linq.Expressions.Expression.Convert(expr_5, typeof(object));
var expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.NotEqual, expr_2, expr_4);
var expr_7 = global::System.Linq.Expressions.Expression.Property(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 expr_6 = global::System.Linq.Expressions.Expression.Property(expr_7, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
var expr_8 = global::System.Linq.Expressions.Expression.Constant("N/A", typeof(string)); // "N/A"
var expr_0 = global::System.Linq.Expressions.Expression.Condition(expr_1, expr_6, expr_8, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(expr_0, p_o);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderSummaryDto._ctor_P0_int_P1_string_P2_decimal.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_OrderSummaryDto
{
// // Constructor projection -- translates to MemberInit
// [Expressive]
// public OrderSummaryDto(int id, string description, decimal total)
// {
// Id = id;
// Description = description;
// Total = total;
// }
static global::System.Linq.Expressions.Expression<global::System.Func<int, string, decimal, global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto>> _ctor_P0_int_P1_string_P2_decimal_Expression()
{
var p_id = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "id");
var p_description = global::System.Linq.Expressions.Expression.Parameter(typeof(string), "description");
var p_total = global::System.Linq.Expressions.Expression.Parameter(typeof(decimal), "total");
var expr_0 = global::System.Linq.Expressions.Expression.New(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetConstructor(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { }, null));
var expr_1 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), p_id);
var expr_2 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetProperty("Description", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), p_description);
var expr_3 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetProperty("Total", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), p_total);
var expr_4 = global::System.Linq.Expressions.Expression.MemberInit(expr_0, expr_1, expr_2, expr_3);
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<int, string, decimal, global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto>>(expr_4, p_id, p_description, p_total);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderComplete.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderComplete { }
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderSummaryDto.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderSummaryDto { }
}
// === 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.OrderComplete).GetMethod("Total", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderComplete", "Total_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression");
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderComplete).GetMethod("TotalWithTax", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderComplete", "TotalWithTax_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression");
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderComplete).GetMethod("CustomerEmail", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderComplete", "CustomerEmail_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression");
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderComplete).GetMethod("GetGrade", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderComplete", "GetGrade_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression");
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderComplete).GetMethod("CustomerNameSafe", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderComplete", "CustomerNameSafe_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression");
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderComplete).GetMethod("GetCategory", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderComplete", "GetCategory_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression");
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderComplete).GetMethod("SafeTag", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderComplete", "SafeTag_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression");
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetConstructor(allFlags, null, new global::System.Type[] { typeof(int), typeof(string), typeof(decimal) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderSummaryDto", "_ctor_P0_int_P1_string_P2_decimal_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, "WsQlopSnw5mxwyZXbqfyEq8BAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto> __Polyfill_Select_3e61_16_5(
this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto> __func)
{
// Source: o => new OrderSummaryDto(o.Id, o.SafeTag(), o.Total())
var i3e6116c5_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
var i3e6116c5_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6116c5_p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).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.OrderComplete).GetMethod("SafeTag", 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[] { i3e6116c5_p_o }); // o.SafeTag()
var i3e6116c5_expr_3 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderComplete).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[] { i3e6116c5_p_o }); // o.Total()
var i3e6116c5_expr_0 = global::System.Linq.Expressions.Expression.New(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetConstructor(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { typeof(int), typeof(string), typeof(decimal) }, null), i3e6116c5_expr_1, i3e6116c5_expr_2, i3e6116c5_expr_3);
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto>>(i3e6116c5_expr_0, i3e6116c5_p_o);
return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order>)source,
__lambda));
}
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "WsQlopSnw5mxwyZXbqfyEoQBAABfX1NuaXBwZXQuY3M=")]
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.CustomerEmail() != null
var i3e6115c5_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
var i3e6115c5_expr_1 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderComplete).GetMethod("CustomerEmail", 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.CustomerEmail()
var i3e6115c5_expr_3 = global::System.Linq.Expressions.Expression.Constant(null, typeof(object)); // null
var i3e6115c5_expr_2 = global::System.Linq.Expressions.Expression.Convert(i3e6115c5_expr_3, typeof(string));
var i3e6115c5_expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.NotEqual, i3e6115c5_expr_1, i3e6115c5_expr_2);
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.CustomerEmail() != null)
.Select(o => new OrderSummaryDto(o.Id, o.SafeTag(), o.Total()))
// Setup
public static class OrderComplete
{
// Simple computed method
[Expressive]
public static decimal Total(this Order o) => o.Items.Sum(i => i.UnitPrice * i.Quantity);
// Composing expressives
[Expressive]
public static decimal TotalWithTax(this Order o) => o.Total() * 1.08m;
// Null-conditional operators -- always generates faithful ternary
[Expressive]
public static string? CustomerEmail(this Order o) => o.Customer?.Email;
// Switch expressions with pattern matching
[Expressive]
public static string GetGrade(this Order o) => o.Items.Count() switch
{
>= 10 => "Premium",
>= 5 => "Standard",
_ => "Budget",
};
// Per-member transformer
[Expressive(Transformers = new[] { typeof(ExpressiveSharp.Transformers.RemoveNullConditionalPatterns) })]
public static string? CustomerNameSafe(this Order o) => o.Customer?.Name;
// Block body (opt-in)
[Expressive(AllowBlockBody = true)]
public static string GetCategory(this Order o)
{
var threshold = o.Items.Count() * 10;
if (threshold > 100) return "Bulk";
return "Regular";
}
// Extension method with null-coalescing
[Expressive]
public static string SafeTag(this Order o) => o.Customer != null ? o.Customer.Name : "N/A";
}
public class OrderSummaryDto
{
public int Id { get; set; }
public string Description { get; set; } = "";
public decimal Total { get; set; }
public OrderSummaryDto() { }
// Constructor projection -- translates to MemberInit
[Expressive]
public OrderSummaryDto(int id, string description, decimal total)
{
Id = id;
Description = description;
Total = total;
}
}Generated SQL:
SELECT "o"."Id", "c"."Name" AS "Description", (
SELECT COALESCE(ef_sum(ef_multiply("l"."UnitPrice", CAST("l"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId") AS "Total"
FROM "Orders" AS "o"
INNER JOIN "Customers" AS "c" ON "o"."CustomerId" = "c"."Id"
WHERE "c"."Email" IS NOT NULL