Switch Expressions
Switch expressions are one of the most useful C# features that ExpressiveSharp enables in expression trees. They are translated to nested ternary expressions at compile time, which LINQ providers map to their native conditional forms (SQL CASE, MongoDB $switch, etc.).
Basic Syntax
Mark any property or method with [Expressive] and use a switch expression in the body:
db
.Products
.Select(p => new { p.Name, Tier = p.GetTier() })
// Setup
public static class ProductExt
{
[Expressive]
public static string GetTier(this Product p) => p.ListPrice switch
{
>= 100m => "Premium",
>= 50m => "Standard",
_ => "Budget",
};
}SELECT "p"."Name", CASE
WHEN ef_compare("p"."ListPrice", '100.0') >= 0 THEN 'Premium'
WHEN ef_compare("p"."ListPrice", '50.0') >= 0 THEN 'Standard'
ELSE 'Budget'
END AS "Tier"
FROM "Products" AS "p"SELECT p."Name", CASE
WHEN p."ListPrice" >= 100.0 THEN 'Premium'
WHEN p."ListPrice" >= 50.0 THEN 'Standard'
ELSE 'Budget'
END AS "Tier"
FROM "Products" AS pSELECT [p].[Name], CASE
WHEN [p].[ListPrice] >= 100.0 THEN N'Premium'
WHEN [p].[ListPrice] >= 50.0 THEN N'Standard'
ELSE N'Budget'
END AS [Tier]
FROM [Products] AS [p]playground.products.Aggregate([
{
"$project" : {
"Name" : "$Name",
"Tier" : {
"$cond" : {
"if" : {
"$gte" : [
"$ListPrice",
{ "$numberDecimal" : "100" }
]
},
"then" : "Premium",
"else" : {
"$cond" : {
"if" : {
"$gte" : [
"$ListPrice",
{ "$numberDecimal" : "50" }
]
},
"then" : "Standard",
"else" : "Budget"
}
}
}
},
"_id" : 0
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_ProductExt.GetTier_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product.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_ProductExt
{
// [Expressive]
// public static string GetTier(this Product p) => p.ListPrice switch
// {
// >= 100m => "Premium",
// >= 50m => "Standard",
// _ => "Budget",
// };
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product, string>> GetTier_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product_Expression()
{
var p_p = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product), "p");
var expr_0 = global::System.Linq.Expressions.Expression.Property(p_p, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product).GetProperty("ListPrice", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // p.ListPrice
var expr_1 = global::System.Linq.Expressions.Expression.Constant("Budget", typeof(string)); // "Budget"
var expr_3 = global::System.Linq.Expressions.Expression.Constant(50m, typeof(decimal)); // 50m
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(100m, typeof(decimal)); // 100m
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.Product, string>>(expr_9, p_p);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_ProductExt.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_ProductExt { }
}
// === 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.ProductExt).GetMethod("GetTier", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_ProductExt", "GetTier_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product_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, "Zf1WsQ4zpjQmQBqqXbg1NoEBAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<T1> __Polyfill_Select_3e61_14_23<T0, T1>(
this global::ExpressiveSharp.IExpressiveQueryable<T0> source,
global::System.Func<T0, T1> __func)
{
// Source: p => new { p.Name, Tier = p.GetTier() }
var i3e6114c23_p_p = global::System.Linq.Expressions.Expression.Parameter(typeof(T0), "p");
var i3e6114c23_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6114c23_p_p, typeof(T0).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // p.Name
var i3e6114c23_expr_2 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.ProductExt).GetMethod("GetTier", 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[] { i3e6114c23_p_p }); // p.GetTier()
var i3e6114c23_expr_3 = typeof(T1).GetConstructors()[0];
var i3e6114c23_expr_0 = global::System.Linq.Expressions.Expression.New(i3e6114c23_expr_3, new global::System.Linq.Expressions.Expression[] { i3e6114c23_expr_1, i3e6114c23_expr_2 }, new global::System.Reflection.MemberInfo[] { typeof(T1).GetProperty("Name"), typeof(T1).GetProperty("Tier") });
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<T0, T1>>(i3e6114c23_expr_0, i3e6114c23_p_p);
return (global::ExpressiveSharp.IExpressiveQueryable<T1>)(object)
global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<T0>)(object)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
.Products
.Select(p => new { p.Name, Tier = p.GetTier() })
// Setup
public static class ProductExt
{
[Expressive]
public static string GetTier(this Product p) => p.ListPrice switch
{
>= 100m => "Premium",
>= 50m => "Standard",
_ => "Budget",
};
}Generated SQL:
SELECT "p"."Name", CASE
WHEN ef_compare("p"."ListPrice", '100.0') >= 0 THEN 'Premium'
WHEN ef_compare("p"."ListPrice", '50.0') >= 0 THEN 'Standard'
ELSE 'Budget'
END AS "Tier"
FROM "Products" AS "p"The source generator produces a chain of conditional expressions that each provider renders in its own dialect (see the tabs above).
Relational Patterns
Relational operators (<, <=, >, >=) work in switch arms:
db
.Products
.Select(p => new { p.Name, Category = p.PriceCategory() })
// Setup
public static class ProductExt
{
[Expressive]
public static string PriceCategory(this Product p) => p.ListPrice switch
{
< 10m => "Cheap",
< 50m => "Moderate",
< 100m => "Expensive",
>= 100m => "Premium",
};
}SELECT "p"."Name", CASE
WHEN ef_compare("p"."ListPrice", '10.0') < 0 THEN 'Cheap'
WHEN ef_compare("p"."ListPrice", '50.0') < 0 THEN 'Moderate'
WHEN ef_compare("p"."ListPrice", '100.0') < 0 THEN 'Expensive'
WHEN ef_compare("p"."ListPrice", '100.0') >= 0 THEN 'Premium'
END AS "Category"
FROM "Products" AS "p"SELECT p."Name", CASE
WHEN p."ListPrice" < 10.0 THEN 'Cheap'
WHEN p."ListPrice" < 50.0 THEN 'Moderate'
WHEN p."ListPrice" < 100.0 THEN 'Expensive'
WHEN p."ListPrice" >= 100.0 THEN 'Premium'
END AS "Category"
FROM "Products" AS pSELECT [p].[Name], CASE
WHEN [p].[ListPrice] < 10.0 THEN N'Cheap'
WHEN [p].[ListPrice] < 50.0 THEN N'Moderate'
WHEN [p].[ListPrice] < 100.0 THEN N'Expensive'
WHEN [p].[ListPrice] >= 100.0 THEN N'Premium'
END AS [Category]
FROM [Products] AS [p]playground.products.Aggregate([
{
"$project" : {
"Name" : "$Name",
"Category" : {
"$cond" : {
"if" : {
"$lt" : [
"$ListPrice",
{ "$numberDecimal" : "10" }
]
},
"then" : "Cheap",
"else" : {
"$cond" : {
"if" : {
"$lt" : [
"$ListPrice",
{ "$numberDecimal" : "50" }
]
},
"then" : "Moderate",
"else" : {
"$cond" : {
"if" : {
"$lt" : [
"$ListPrice",
{ "$numberDecimal" : "100" }
]
},
"then" : "Expensive",
"else" : {
"$cond" : {
"if" : {
"$gte" : [
"$ListPrice",
{ "$numberDecimal" : "100" }
]
},
"then" : "Premium",
"else" : null
}
}
}
}
}
}
}
},
"_id" : 0
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_ProductExt.PriceCategory_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product.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_ProductExt
{
// [Expressive]
// public static string PriceCategory(this Product p) => p.ListPrice switch
// {
// < 10m => "Cheap",
// < 50m => "Moderate",
// < 100m => "Expensive",
// >= 100m => "Premium",
// };
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product, string>> PriceCategory_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product_Expression()
{
var p_p = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product), "p");
var expr_0 = global::System.Linq.Expressions.Expression.Property(p_p, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product).GetProperty("ListPrice", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // p.ListPrice
var expr_1 = global::System.Linq.Expressions.Expression.Default(typeof(string));
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("Premium", typeof(string)); // "Premium"
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(100m, typeof(decimal)); // 100m
var expr_6 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.LessThan, expr_0, expr_7);
var expr_8 = global::System.Linq.Expressions.Expression.Constant("Expensive", typeof(string)); // "Expensive"
var expr_9 = global::System.Linq.Expressions.Expression.Condition(expr_6, expr_8, expr_5, typeof(string));
var expr_11 = global::System.Linq.Expressions.Expression.Constant(50m, typeof(decimal)); // 50m
var expr_10 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.LessThan, expr_0, expr_11);
var expr_12 = global::System.Linq.Expressions.Expression.Constant("Moderate", typeof(string)); // "Moderate"
var expr_13 = global::System.Linq.Expressions.Expression.Condition(expr_10, expr_12, expr_9, typeof(string));
var expr_15 = global::System.Linq.Expressions.Expression.Constant(10m, typeof(decimal)); // 10m
var expr_14 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.LessThan, expr_0, expr_15);
var expr_16 = global::System.Linq.Expressions.Expression.Constant("Cheap", typeof(string)); // "Cheap"
var expr_17 = global::System.Linq.Expressions.Expression.Condition(expr_14, expr_16, expr_13, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product, string>>(expr_17, p_p);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_ProductExt.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_ProductExt { }
}
// === 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.ProductExt).GetMethod("PriceCategory", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_ProductExt", "PriceCategory_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product_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, "W6QtYdNhhdDnCroKTIgoFoEBAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<T1> __Polyfill_Select_3e61_14_23<T0, T1>(
this global::ExpressiveSharp.IExpressiveQueryable<T0> source,
global::System.Func<T0, T1> __func)
{
// Source: p => new { p.Name, Category = p.PriceCategory() }
var i3e6114c23_p_p = global::System.Linq.Expressions.Expression.Parameter(typeof(T0), "p");
var i3e6114c23_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6114c23_p_p, typeof(T0).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // p.Name
var i3e6114c23_expr_2 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.ProductExt).GetMethod("PriceCategory", 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[] { i3e6114c23_p_p }); // p.PriceCategory()
var i3e6114c23_expr_3 = typeof(T1).GetConstructors()[0];
var i3e6114c23_expr_0 = global::System.Linq.Expressions.Expression.New(i3e6114c23_expr_3, new global::System.Linq.Expressions.Expression[] { i3e6114c23_expr_1, i3e6114c23_expr_2 }, new global::System.Reflection.MemberInfo[] { typeof(T1).GetProperty("Name"), typeof(T1).GetProperty("Category") });
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<T0, T1>>(i3e6114c23_expr_0, i3e6114c23_p_p);
return (global::ExpressiveSharp.IExpressiveQueryable<T1>)(object)
global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<T0>)(object)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
.Products
.Select(p => new { p.Name, Category = p.PriceCategory() })
// Setup
public static class ProductExt
{
[Expressive]
public static string PriceCategory(this Product p) => p.ListPrice switch
{
< 10m => "Cheap",
< 50m => "Moderate",
< 100m => "Expensive",
>= 100m => "Premium",
};
}Generated SQL:
SELECT "p"."Name", CASE
WHEN ef_compare("p"."ListPrice", '10.0') < 0 THEN 'Cheap'
WHEN ef_compare("p"."ListPrice", '50.0') < 0 THEN 'Moderate'
WHEN ef_compare("p"."ListPrice", '100.0') < 0 THEN 'Expensive'
WHEN ef_compare("p"."ListPrice", '100.0') >= 0 THEN 'Premium'
END AS "Category"
FROM "Products" AS "p"WARNING
Without a discard arm (_), the generated expression has no fallback. If no arm matches at runtime, a SwitchExpressionException would be thrown in C#. In SQL, the result is NULL (the ELSE clause is omitted). Always include a discard arm for safety.
and / or Combinators
Combine patterns with and and or for range checks and alternatives:
db
.Products
.Select(p => new { p.Name, Band = p.GetBand() })
// Setup
public static class ProductExt
{
[Expressive]
public static string GetBand(this Product p) => p.StockQuantity switch
{
>= 90 and <= 100 => "Excellent",
>= 70 and < 90 => "Good",
>= 50 and < 70 => "Average",
_ => "Poor",
};
}SELECT "p"."Name", CASE
WHEN "p"."StockQuantity" >= 90 AND "p"."StockQuantity" <= 100 THEN 'Excellent'
WHEN "p"."StockQuantity" >= 70 AND "p"."StockQuantity" < 90 THEN 'Good'
WHEN "p"."StockQuantity" >= 50 AND "p"."StockQuantity" < 70 THEN 'Average'
ELSE 'Poor'
END AS "Band"
FROM "Products" AS "p"SELECT p."Name", CASE
WHEN p."StockQuantity" >= 90 AND p."StockQuantity" <= 100 THEN 'Excellent'
WHEN p."StockQuantity" >= 70 AND p."StockQuantity" < 90 THEN 'Good'
WHEN p."StockQuantity" >= 50 AND p."StockQuantity" < 70 THEN 'Average'
ELSE 'Poor'
END AS "Band"
FROM "Products" AS pSELECT [p].[Name], CASE
WHEN [p].[StockQuantity] >= 90 AND [p].[StockQuantity] <= 100 THEN N'Excellent'
WHEN [p].[StockQuantity] >= 70 AND [p].[StockQuantity] < 90 THEN N'Good'
WHEN [p].[StockQuantity] >= 50 AND [p].[StockQuantity] < 70 THEN N'Average'
ELSE N'Poor'
END AS [Band]
FROM [Products] AS [p]playground.products.Aggregate([
{
"$project" : {
"Name" : "$Name",
"Band" : {
"$cond" : {
"if" : {
"$and" : [
{
"$gte" : ["$StockQuantity", 90]
},
{
"$lte" : ["$StockQuantity", 100]
}
]
},
"then" : "Excellent",
"else" : {
"$cond" : {
"if" : {
"$and" : [
{
"$gte" : ["$StockQuantity", 70]
},
{
"$lt" : ["$StockQuantity", 90]
}
]
},
"then" : "Good",
"else" : {
"$cond" : {
"if" : {
"$and" : [
{
"$gte" : ["$StockQuantity", 50]
},
{
"$lt" : ["$StockQuantity", 70]
}
]
},
"then" : "Average",
"else" : "Poor"
}
}
}
}
}
},
"_id" : 0
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_ProductExt.GetBand_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product.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_ProductExt
{
// [Expressive]
// public static string GetBand(this Product p) => p.StockQuantity switch
// {
// >= 90 and <= 100 => "Excellent",
// >= 70 and < 90 => "Good",
// >= 50 and < 70 => "Average",
// _ => "Poor",
// };
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product, string>> GetBand_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product_Expression()
{
var p_p = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product), "p");
var expr_0 = global::System.Linq.Expressions.Expression.Property(p_p, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product).GetProperty("StockQuantity", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // p.StockQuantity
var expr_1 = global::System.Linq.Expressions.Expression.Constant("Poor", typeof(string)); // "Poor"
var expr_4 = global::System.Linq.Expressions.Expression.Constant(50, typeof(int)); // 50
var expr_3 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_0, expr_4);
var expr_6 = global::System.Linq.Expressions.Expression.Constant(70, typeof(int)); // 70
var expr_5 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.LessThan, expr_0, expr_6);
var expr_2 = global::System.Linq.Expressions.Expression.AndAlso(expr_3, expr_5);
var expr_7 = global::System.Linq.Expressions.Expression.Constant("Average", typeof(string)); // "Average"
var expr_8 = global::System.Linq.Expressions.Expression.Condition(expr_2, expr_7, expr_1, typeof(string));
var expr_11 = global::System.Linq.Expressions.Expression.Constant(70, typeof(int)); // 70
var expr_10 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_0, expr_11);
var expr_13 = global::System.Linq.Expressions.Expression.Constant(90, typeof(int)); // 90
var expr_12 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.LessThan, expr_0, expr_13);
var expr_9 = global::System.Linq.Expressions.Expression.AndAlso(expr_10, expr_12);
var expr_14 = global::System.Linq.Expressions.Expression.Constant("Good", typeof(string)); // "Good"
var expr_15 = global::System.Linq.Expressions.Expression.Condition(expr_9, expr_14, expr_8, typeof(string));
var expr_18 = global::System.Linq.Expressions.Expression.Constant(90, typeof(int)); // 90
var expr_17 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_0, expr_18);
var expr_20 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
var expr_19 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.LessThanOrEqual, expr_0, expr_20);
var expr_16 = global::System.Linq.Expressions.Expression.AndAlso(expr_17, expr_19);
var expr_21 = global::System.Linq.Expressions.Expression.Constant("Excellent", typeof(string)); // "Excellent"
var expr_22 = global::System.Linq.Expressions.Expression.Condition(expr_16, expr_21, expr_15, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product, string>>(expr_22, p_p);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_ProductExt.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_ProductExt { }
}
// === 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.ProductExt).GetMethod("GetBand", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_ProductExt", "GetBand_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product_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, "jD4/2uc49GK8Y/nMOohyDYEBAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<T1> __Polyfill_Select_3e61_14_23<T0, T1>(
this global::ExpressiveSharp.IExpressiveQueryable<T0> source,
global::System.Func<T0, T1> __func)
{
// Source: p => new { p.Name, Band = p.GetBand() }
var i3e6114c23_p_p = global::System.Linq.Expressions.Expression.Parameter(typeof(T0), "p");
var i3e6114c23_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6114c23_p_p, typeof(T0).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // p.Name
var i3e6114c23_expr_2 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.ProductExt).GetMethod("GetBand", 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[] { i3e6114c23_p_p }); // p.GetBand()
var i3e6114c23_expr_3 = typeof(T1).GetConstructors()[0];
var i3e6114c23_expr_0 = global::System.Linq.Expressions.Expression.New(i3e6114c23_expr_3, new global::System.Linq.Expressions.Expression[] { i3e6114c23_expr_1, i3e6114c23_expr_2 }, new global::System.Reflection.MemberInfo[] { typeof(T1).GetProperty("Name"), typeof(T1).GetProperty("Band") });
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<T0, T1>>(i3e6114c23_expr_0, i3e6114c23_p_p);
return (global::ExpressiveSharp.IExpressiveQueryable<T1>)(object)
global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<T0>)(object)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
.Products
.Select(p => new { p.Name, Band = p.GetBand() })
// Setup
public static class ProductExt
{
[Expressive]
public static string GetBand(this Product p) => p.StockQuantity switch
{
>= 90 and <= 100 => "Excellent",
>= 70 and < 90 => "Good",
>= 50 and < 70 => "Average",
_ => "Poor",
};
}Generated SQL:
SELECT "p"."Name", CASE
WHEN "p"."StockQuantity" >= 90 AND "p"."StockQuantity" <= 100 THEN 'Excellent'
WHEN "p"."StockQuantity" >= 70 AND "p"."StockQuantity" < 90 THEN 'Good'
WHEN "p"."StockQuantity" >= 50 AND "p"."StockQuantity" < 70 THEN 'Average'
ELSE 'Poor'
END AS "Band"
FROM "Products" AS "p"Using or for alternative values:
db
.Orders
.Select(o => new { o.Id, Type = o.GetDayType() })
// Setup
public static class OrderExt
{
[Expressive]
public static string GetDayType(this Order o) => (int)o.PlacedAt.DayOfWeek switch
{
0 or 6 => "Weekend",
_ => "Weekday",
};
}SELECT "o"."Id", CASE
WHEN CAST(strftime('%w', "o"."PlacedAt") AS INTEGER) = 0 OR CAST(strftime('%w', "o"."PlacedAt") AS INTEGER) = 6 THEN 'Weekend'
ELSE 'Weekday'
END AS "Type"
FROM "Orders" AS "o"SELECT o."Id", CASE
WHEN floor(date_part('dow', o."PlacedAt"))::int = 0 OR floor(date_part('dow', o."PlacedAt"))::int = 6 THEN 'Weekend'
ELSE 'Weekday'
END AS "Type"
FROM "Orders" AS oSELECT [o].[Id], [o].[PlacedAt]
FROM [Orders] AS [o]playground.orders.Aggregate([
{
"$project" : {
"_id" : "$_id",
"Type" : {
"$cond" : {
"if" : {
"$or" : [
{
"$eq" : [
{
"$subtract" : [
{ "$dayOfWeek" : "$PlacedAt" },
1
]
},
0
]
},
{
"$eq" : [
{
"$subtract" : [
{ "$dayOfWeek" : "$PlacedAt" },
1
]
},
6
]
}
]
},
"then" : "Weekend",
"else" : "Weekday"
}
}
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_OrderExt.GetDayType_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 GetDayType(this Order o) => (int)o.PlacedAt.DayOfWeek switch
// {
// 0 or 6 => "Weekend",
// _ => "Weekday",
// };
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>> GetDayType_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_2 = global::System.Linq.Expressions.Expression.Property(p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("PlacedAt", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.PlacedAt
var expr_1 = global::System.Linq.Expressions.Expression.Property(expr_2, typeof(global::System.DateTime).GetProperty("DayOfWeek", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
var expr_0 = global::System.Linq.Expressions.Expression.Convert(expr_1, typeof(int));
var expr_3 = global::System.Linq.Expressions.Expression.Constant("Weekday", typeof(string)); // "Weekday"
var expr_6 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
var expr_5 = global::System.Linq.Expressions.Expression.Equal(expr_0, expr_6);
var expr_8 = global::System.Linq.Expressions.Expression.Constant(6, typeof(int)); // 6
var expr_7 = global::System.Linq.Expressions.Expression.Equal(expr_0, expr_8);
var expr_4 = global::System.Linq.Expressions.Expression.OrElse(expr_5, expr_7);
var expr_9 = global::System.Linq.Expressions.Expression.Constant("Weekend", typeof(string)); // "Weekend"
var expr_10 = global::System.Linq.Expressions.Expression.Condition(expr_4, expr_9, expr_3, 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_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("GetDayType", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderExt", "GetDayType_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, "OK5nD10Mw22YcqjjspYKfX8BAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<T1> __Polyfill_Select_3e61_14_21<T0, T1>(
this global::ExpressiveSharp.IExpressiveQueryable<T0> source,
global::System.Func<T0, T1> __func)
{
// Source: o => new { o.Id, Type = o.GetDayType() }
var i3e6114c21_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(T0), "o");
var i3e6114c21_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6114c21_p_o, typeof(T0).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Id
var i3e6114c21_expr_2 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderExt).GetMethod("GetDayType", 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[] { i3e6114c21_p_o }); // o.GetDayType()
var i3e6114c21_expr_3 = typeof(T1).GetConstructors()[0];
var i3e6114c21_expr_0 = global::System.Linq.Expressions.Expression.New(i3e6114c21_expr_3, new global::System.Linq.Expressions.Expression[] { i3e6114c21_expr_1, i3e6114c21_expr_2 }, new global::System.Reflection.MemberInfo[] { typeof(T1).GetProperty("Id"), typeof(T1).GetProperty("Type") });
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<T0, T1>>(i3e6114c21_expr_0, i3e6114c21_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));
}
}
}
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 => new { o.Id, Type = o.GetDayType() })
// Setup
public static class OrderExt
{
[Expressive]
public static string GetDayType(this Order o) => (int)o.PlacedAt.DayOfWeek switch
{
0 or 6 => "Weekend",
_ => "Weekday",
};
}Generated SQL:
SELECT "o"."Id", CASE
WHEN CAST(strftime('%w', "o"."PlacedAt") AS INTEGER) = 0 OR CAST(strftime('%w', "o"."PlacedAt") AS INTEGER) = 6 THEN 'Weekend'
ELSE 'Weekday'
END AS "Type"
FROM "Orders" AS "o"when Guards
Guards add additional boolean conditions to switch arms:
db
.LineItems
.Select(i => new { i.Id, Tag = i.Classify() })
// Setup
public static class LineItemExt
{
[Expressive]
public static string Classify(this LineItem i) => i.Quantity switch
{
> 100 when i.UnitPrice < 10m => "Bulk Bargain",
> 100 => "Bulk Order",
> 0 => "Standard",
_ => "Empty",
};
}SELECT "l"."Id", CASE
WHEN "l"."Quantity" > 100 AND ef_compare("l"."UnitPrice", '10.0') < 0 THEN 'Bulk Bargain'
WHEN "l"."Quantity" > 100 THEN 'Bulk Order'
WHEN "l"."Quantity" > 0 THEN 'Standard'
ELSE 'Empty'
END AS "Tag"
FROM "LineItems" AS "l"SELECT l."Id", CASE
WHEN l."Quantity" > 100 AND l."UnitPrice" < 10.0 THEN 'Bulk Bargain'
WHEN l."Quantity" > 100 THEN 'Bulk Order'
WHEN l."Quantity" > 0 THEN 'Standard'
ELSE 'Empty'
END AS "Tag"
FROM "LineItems" AS lSELECT [l].[Id], CASE
WHEN [l].[Quantity] > 100 AND [l].[UnitPrice] < 10.0 THEN N'Bulk Bargain'
WHEN [l].[Quantity] > 100 THEN N'Bulk Order'
WHEN [l].[Quantity] > 0 THEN N'Standard'
ELSE N'Empty'
END AS [Tag]
FROM [LineItems] AS [l]playground.line_items.Aggregate([
{
"$project" : {
"_id" : "$_id",
"Tag" : {
"$cond" : {
"if" : {
"$and" : [
{
"$gt" : ["$Quantity", 100]
},
{
"$lt" : [
"$UnitPrice",
{ "$numberDecimal" : "10" }
]
}
]
},
"then" : "Bulk Bargain",
"else" : {
"$cond" : {
"if" : {
"$gt" : ["$Quantity", 100]
},
"then" : "Bulk Order",
"else" : {
"$cond" : {
"if" : {
"$gt" : ["$Quantity", 0]
},
"then" : "Standard",
"else" : "Empty"
}
}
}
}
}
}
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_LineItemExt.Classify_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_LineItem.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_LineItemExt
{
// [Expressive]
// public static string Classify(this LineItem i) => i.Quantity switch
// {
// > 100 when i.UnitPrice < 10m => "Bulk Bargain",
// > 100 => "Bulk Order",
// > 0 => "Standard",
// _ => "Empty",
// };
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem, string>> Classify_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_LineItem_Expression()
{
var p_i = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem), "i");
var expr_0 = global::System.Linq.Expressions.Expression.Property(p_i, 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_1 = global::System.Linq.Expressions.Expression.Constant("Empty", typeof(string)); // "Empty"
var expr_3 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
var expr_2 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, 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(100, typeof(int)); // 100
var expr_6 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, expr_0, expr_7);
var expr_8 = global::System.Linq.Expressions.Expression.Constant("Bulk Order", typeof(string)); // "Bulk Order"
var expr_9 = global::System.Linq.Expressions.Expression.Condition(expr_6, expr_8, expr_5, typeof(string));
var expr_11 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
var expr_10 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, expr_0, expr_11);
var expr_13 = global::System.Linq.Expressions.Expression.Property(p_i, 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_14 = global::System.Linq.Expressions.Expression.Constant(10m, typeof(decimal)); // 10m
var expr_12 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.LessThan, expr_13, expr_14);
var expr_15 = global::System.Linq.Expressions.Expression.AndAlso(expr_10, expr_12);
var expr_16 = global::System.Linq.Expressions.Expression.Constant("Bulk Bargain", typeof(string)); // "Bulk Bargain"
var expr_17 = global::System.Linq.Expressions.Expression.Condition(expr_15, expr_16, expr_9, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem, string>>(expr_17, p_i);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_LineItemExt.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_LineItemExt { }
}
// === 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.LineItemExt).GetMethod("Classify", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_LineItemExt", "Classify_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_LineItem_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, "rGTCBxuwyxBnN3do6Abr/IIBAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<T1> __Polyfill_Select_3e61_14_24<T0, T1>(
this global::ExpressiveSharp.IExpressiveQueryable<T0> source,
global::System.Func<T0, T1> __func)
{
// Source: i => new { i.Id, Tag = i.Classify() }
var i3e6114c24_p_i = global::System.Linq.Expressions.Expression.Parameter(typeof(T0), "i");
var i3e6114c24_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6114c24_p_i, typeof(T0).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // i.Id
var i3e6114c24_expr_2 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.LineItemExt).GetMethod("Classify", 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[] { i3e6114c24_p_i }); // i.Classify()
var i3e6114c24_expr_3 = typeof(T1).GetConstructors()[0];
var i3e6114c24_expr_0 = global::System.Linq.Expressions.Expression.New(i3e6114c24_expr_3, new global::System.Linq.Expressions.Expression[] { i3e6114c24_expr_1, i3e6114c24_expr_2 }, new global::System.Reflection.MemberInfo[] { typeof(T1).GetProperty("Id"), typeof(T1).GetProperty("Tag") });
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<T0, T1>>(i3e6114c24_expr_0, i3e6114c24_p_i);
return (global::ExpressiveSharp.IExpressiveQueryable<T1>)(object)
global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<T0>)(object)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
.LineItems
.Select(i => new { i.Id, Tag = i.Classify() })
// Setup
public static class LineItemExt
{
[Expressive]
public static string Classify(this LineItem i) => i.Quantity switch
{
> 100 when i.UnitPrice < 10m => "Bulk Bargain",
> 100 => "Bulk Order",
> 0 => "Standard",
_ => "Empty",
};
}Generated SQL:
SELECT "l"."Id", CASE
WHEN "l"."Quantity" > 100 AND ef_compare("l"."UnitPrice", '10.0') < 0 THEN 'Bulk Bargain'
WHEN "l"."Quantity" > 100 THEN 'Bulk Order'
WHEN "l"."Quantity" > 0 THEN 'Standard'
ELSE 'Empty'
END AS "Tag"
FROM "LineItems" AS "l"The guard condition is combined with the pattern using && in the generated expression, which each provider renders as part of its conditional form.
Type Patterns with Declaration Variables
Switch arms can match on type and bind the result to a variable:
[Expressive]
public static string Describe(this Animal animal) => animal switch
{
Dog d => $"Dog named {d.Name}",
Cat c => $"Cat: {c.Breed}",
_ => "Unknown animal",
};The generator produces type-check and cast expressions:
animal is Dog ? $"Dog named {((Dog)animal).Name}"
: animal is Cat ? $"Cat: {((Cat)animal).Breed}"
: "Unknown animal"INFO
Declaration variables work within switch arms. The generated expression binds them via cast expressions. This is particularly useful for EF Core inheritance hierarchies (TPH, TPT, TPC).
Constant Patterns
Match against specific constant values:
db
.Orders
.Select(o => new { o.Id, Label = o.StatusLabel() })
// Setup
public static class OrderExt
{
[Expressive]
public static string StatusLabel(this Order o) => (int)o.Status switch
{
0 => "Pending",
1 => "Paid",
2 => "Shipped",
3 => "Delivered",
4 => "Refunded",
_ => "Unknown",
};
}SELECT "o"."Id", CASE
WHEN "o"."Status" = 0 THEN 'Pending'
WHEN "o"."Status" = 1 THEN 'Paid'
WHEN "o"."Status" = 2 THEN 'Shipped'
WHEN "o"."Status" = 3 THEN 'Delivered'
WHEN "o"."Status" = 4 THEN 'Refunded'
ELSE 'Unknown'
END AS "Label"
FROM "Orders" AS "o"SELECT o."Id", CASE
WHEN o."Status" = 0 THEN 'Pending'
WHEN o."Status" = 1 THEN 'Paid'
WHEN o."Status" = 2 THEN 'Shipped'
WHEN o."Status" = 3 THEN 'Delivered'
WHEN o."Status" = 4 THEN 'Refunded'
ELSE 'Unknown'
END AS "Label"
FROM "Orders" AS oSELECT [o].[Id], CASE
WHEN [o].[Status] = 0 THEN N'Pending'
WHEN [o].[Status] = 1 THEN N'Paid'
WHEN [o].[Status] = 2 THEN N'Shipped'
WHEN [o].[Status] = 3 THEN N'Delivered'
WHEN [o].[Status] = 4 THEN N'Refunded'
ELSE N'Unknown'
END AS [Label]
FROM [Orders] AS [o]playground.orders.Aggregate([
{
"$project" : {
"_id" : "$_id",
"Label" : {
"$cond" : {
"if" : {
"$eq" : ["$Status", 0]
},
"then" : "Pending",
"else" : {
"$cond" : {
"if" : {
"$eq" : ["$Status", 1]
},
"then" : "Paid",
"else" : {
"$cond" : {
"if" : {
"$eq" : ["$Status", 2]
},
"then" : "Shipped",
"else" : {
"$cond" : {
"if" : {
"$eq" : ["$Status", 3]
},
"then" : "Delivered",
"else" : {
"$cond" : {
"if" : {
"$eq" : ["$Status", 4]
},
"then" : "Refunded",
"else" : "Unknown"
}
}
}
}
}
}
}
}
}
}
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_OrderExt.StatusLabel_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 StatusLabel(this Order o) => (int)o.Status switch
// {
// 0 => "Pending",
// 1 => "Paid",
// 2 => "Shipped",
// 3 => "Delivered",
// 4 => "Refunded",
// _ => "Unknown",
// };
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>> StatusLabel_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("Status", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Status
var expr_0 = global::System.Linq.Expressions.Expression.Convert(expr_1, typeof(int));
var expr_2 = global::System.Linq.Expressions.Expression.Constant("Unknown", typeof(string)); // "Unknown"
var expr_4 = global::System.Linq.Expressions.Expression.Constant(4, typeof(int)); // 4
var expr_3 = global::System.Linq.Expressions.Expression.Equal(expr_0, expr_4);
var expr_5 = global::System.Linq.Expressions.Expression.Constant("Refunded", typeof(string)); // "Refunded"
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(3, typeof(int)); // 3
var expr_7 = global::System.Linq.Expressions.Expression.Equal(expr_0, expr_8);
var expr_9 = global::System.Linq.Expressions.Expression.Constant("Delivered", typeof(string)); // "Delivered"
var expr_10 = global::System.Linq.Expressions.Expression.Condition(expr_7, expr_9, expr_6, typeof(string));
var expr_12 = global::System.Linq.Expressions.Expression.Constant(2, typeof(int)); // 2
var expr_11 = global::System.Linq.Expressions.Expression.Equal(expr_0, expr_12);
var expr_13 = global::System.Linq.Expressions.Expression.Constant("Shipped", typeof(string)); // "Shipped"
var expr_14 = global::System.Linq.Expressions.Expression.Condition(expr_11, expr_13, expr_10, typeof(string));
var expr_16 = global::System.Linq.Expressions.Expression.Constant(1, typeof(int)); // 1
var expr_15 = global::System.Linq.Expressions.Expression.Equal(expr_0, expr_16);
var expr_17 = global::System.Linq.Expressions.Expression.Constant("Paid", typeof(string)); // "Paid"
var expr_18 = global::System.Linq.Expressions.Expression.Condition(expr_15, expr_17, expr_14, typeof(string));
var expr_20 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
var expr_19 = global::System.Linq.Expressions.Expression.Equal(expr_0, expr_20);
var expr_21 = global::System.Linq.Expressions.Expression.Constant("Pending", typeof(string)); // "Pending"
var expr_22 = global::System.Linq.Expressions.Expression.Condition(expr_19, expr_21, expr_18, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, string>>(expr_22, 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("StatusLabel", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderExt", "StatusLabel_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, "XadnDf3w0XwXKSdUA9+PHX8BAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<T1> __Polyfill_Select_3e61_14_21<T0, T1>(
this global::ExpressiveSharp.IExpressiveQueryable<T0> source,
global::System.Func<T0, T1> __func)
{
// Source: o => new { o.Id, Label = o.StatusLabel() }
var i3e6114c21_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(T0), "o");
var i3e6114c21_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6114c21_p_o, typeof(T0).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Id
var i3e6114c21_expr_2 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderExt).GetMethod("StatusLabel", 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[] { i3e6114c21_p_o }); // o.StatusLabel()
var i3e6114c21_expr_3 = typeof(T1).GetConstructors()[0];
var i3e6114c21_expr_0 = global::System.Linq.Expressions.Expression.New(i3e6114c21_expr_3, new global::System.Linq.Expressions.Expression[] { i3e6114c21_expr_1, i3e6114c21_expr_2 }, new global::System.Reflection.MemberInfo[] { typeof(T1).GetProperty("Id"), typeof(T1).GetProperty("Label") });
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<T0, T1>>(i3e6114c21_expr_0, i3e6114c21_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));
}
}
}
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 => new { o.Id, Label = o.StatusLabel() })
// Setup
public static class OrderExt
{
[Expressive]
public static string StatusLabel(this Order o) => (int)o.Status switch
{
0 => "Pending",
1 => "Paid",
2 => "Shipped",
3 => "Delivered",
4 => "Refunded",
_ => "Unknown",
};
}Generated SQL:
SELECT "o"."Id", CASE
WHEN "o"."Status" = 0 THEN 'Pending'
WHEN "o"."Status" = 1 THEN 'Paid'
WHEN "o"."Status" = 2 THEN 'Shipped'
WHEN "o"."Status" = 3 THEN 'Delivered'
WHEN "o"."Status" = 4 THEN 'Refunded'
ELSE 'Unknown'
END AS "Label"
FROM "Orders" AS "o"Nested Switch Expressions
Switch expressions can be nested for multi-dimensional classification:
db
.Products
.Select(p => new { p.Name, Priority = p.GetPriority() })
// Setup
public static class ProductExt
{
[Expressive]
public static string GetPriority(this Product p) => p.Category switch
{
"Electronics" => p.ListPrice switch
{
>= 500m => "High",
>= 100m => "Medium",
_ => "Low",
},
"Food" => "Standard",
_ => "Default",
};
}SELECT "p"."Name", CASE
WHEN "p"."Category" = 'Electronics' THEN CASE
WHEN ef_compare("p"."ListPrice", '500.0') >= 0 THEN 'High'
WHEN ef_compare("p"."ListPrice", '100.0') >= 0 THEN 'Medium'
ELSE 'Low'
END
WHEN "p"."Category" = 'Food' THEN 'Standard'
ELSE 'Default'
END AS "Priority"
FROM "Products" AS "p"SELECT p."Name", CASE
WHEN p."Category" = 'Electronics' THEN CASE
WHEN p."ListPrice" >= 500.0 THEN 'High'
WHEN p."ListPrice" >= 100.0 THEN 'Medium'
ELSE 'Low'
END
WHEN p."Category" = 'Food' THEN 'Standard'
ELSE 'Default'
END AS "Priority"
FROM "Products" AS pSELECT [p].[Name], CASE
WHEN [p].[Category] = N'Electronics' THEN CASE
WHEN [p].[ListPrice] >= 500.0 THEN N'High'
WHEN [p].[ListPrice] >= 100.0 THEN N'Medium'
ELSE N'Low'
END
WHEN [p].[Category] = N'Food' THEN N'Standard'
ELSE N'Default'
END AS [Priority]
FROM [Products] AS [p]playground.products.Aggregate([
{
"$project" : {
"Name" : "$Name",
"Priority" : {
"$cond" : {
"if" : {
"$eq" : ["$Category", "Electronics"]
},
"then" : {
"$cond" : {
"if" : {
"$gte" : [
"$ListPrice",
{ "$numberDecimal" : "500" }
]
},
"then" : "High",
"else" : {
"$cond" : {
"if" : {
"$gte" : [
"$ListPrice",
{ "$numberDecimal" : "100" }
]
},
"then" : "Medium",
"else" : "Low"
}
}
}
},
"else" : {
"$cond" : {
"if" : {
"$eq" : ["$Category", "Food"]
},
"then" : "Standard",
"else" : "Default"
}
}
}
},
"_id" : 0
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_ProductExt.GetPriority_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product.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_ProductExt
{
// [Expressive]
// public static string GetPriority(this Product p) => p.Category switch
// {
// "Electronics" => p.ListPrice switch
// {
// >= 500m => "High",
// >= 100m => "Medium",
// _ => "Low",
// },
// "Food" => "Standard",
// _ => "Default",
// };
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product, string>> GetPriority_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product_Expression()
{
var p_p = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product), "p");
var expr_0 = global::System.Linq.Expressions.Expression.Property(p_p, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product).GetProperty("Category", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // p.Category
var expr_1 = global::System.Linq.Expressions.Expression.Constant("Default", typeof(string)); // "Default"
var expr_3 = global::System.Linq.Expressions.Expression.Constant("Food", typeof(string)); // "Food"
var expr_2 = global::System.Linq.Expressions.Expression.Equal(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("Electronics", typeof(string)); // "Electronics"
var expr_6 = global::System.Linq.Expressions.Expression.Equal(expr_0, expr_7);
var expr_8 = global::System.Linq.Expressions.Expression.Property(p_p, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product).GetProperty("ListPrice", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // p.ListPrice
var expr_9 = global::System.Linq.Expressions.Expression.Constant("Low", typeof(string)); // "Low"
var expr_11 = global::System.Linq.Expressions.Expression.Constant(100m, typeof(decimal)); // 100m
var expr_10 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_8, expr_11);
var expr_12 = global::System.Linq.Expressions.Expression.Constant("Medium", typeof(string)); // "Medium"
var expr_13 = global::System.Linq.Expressions.Expression.Condition(expr_10, expr_12, expr_9, typeof(string));
var expr_15 = global::System.Linq.Expressions.Expression.Constant(500m, typeof(decimal)); // 500m
var expr_14 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_8, expr_15);
var expr_16 = global::System.Linq.Expressions.Expression.Constant("High", typeof(string)); // "High"
var expr_17 = global::System.Linq.Expressions.Expression.Condition(expr_14, expr_16, expr_13, typeof(string));
var expr_18 = global::System.Linq.Expressions.Expression.Condition(expr_6, expr_17, expr_5, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product, string>>(expr_18, p_p);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_ProductExt.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_ProductExt { }
}
// === 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.ProductExt).GetMethod("GetPriority", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_ProductExt", "GetPriority_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Product_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, "g+kyLQ8m2xlz0solLkv2AYEBAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<T1> __Polyfill_Select_3e61_14_23<T0, T1>(
this global::ExpressiveSharp.IExpressiveQueryable<T0> source,
global::System.Func<T0, T1> __func)
{
// Source: p => new { p.Name, Priority = p.GetPriority() }
var i3e6114c23_p_p = global::System.Linq.Expressions.Expression.Parameter(typeof(T0), "p");
var i3e6114c23_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6114c23_p_p, typeof(T0).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // p.Name
var i3e6114c23_expr_2 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.ProductExt).GetMethod("GetPriority", 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[] { i3e6114c23_p_p }); // p.GetPriority()
var i3e6114c23_expr_3 = typeof(T1).GetConstructors()[0];
var i3e6114c23_expr_0 = global::System.Linq.Expressions.Expression.New(i3e6114c23_expr_3, new global::System.Linq.Expressions.Expression[] { i3e6114c23_expr_1, i3e6114c23_expr_2 }, new global::System.Reflection.MemberInfo[] { typeof(T1).GetProperty("Name"), typeof(T1).GetProperty("Priority") });
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<T0, T1>>(i3e6114c23_expr_0, i3e6114c23_p_p);
return (global::ExpressiveSharp.IExpressiveQueryable<T1>)(object)
global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<T0>)(object)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
.Products
.Select(p => new { p.Name, Priority = p.GetPriority() })
// Setup
public static class ProductExt
{
[Expressive]
public static string GetPriority(this Product p) => p.Category switch
{
"Electronics" => p.ListPrice switch
{
>= 500m => "High",
>= 100m => "Medium",
_ => "Low",
},
"Food" => "Standard",
_ => "Default",
};
}Generated SQL:
SELECT "p"."Name", CASE
WHEN "p"."Category" = 'Electronics' THEN CASE
WHEN ef_compare("p"."ListPrice", '500.0') >= 0 THEN 'High'
WHEN ef_compare("p"."ListPrice", '100.0') >= 0 THEN 'Medium'
ELSE 'Low'
END
WHEN "p"."Category" = 'Food' THEN 'Standard'
ELSE 'Default'
END AS "Priority"
FROM "Products" AS "p"Property Patterns in Switch Arms
Match against an object's properties:
db
.LineItems
.Select(i => new { i.Id, Tag = i.ClassifyItem() })
// Setup
public static class LineItemExt
{
[Expressive]
public static string ClassifyItem(this LineItem i) => i switch
{
{ Quantity: > 100, UnitPrice: >= 50m } => "Large Premium",
{ Quantity: > 100 } => "Large Standard",
{ UnitPrice: >= 50m } => "Small Premium",
_ => "Small Standard",
};
}SELECT "l"."Id", CASE
WHEN "l"."Quantity" > 100 AND ef_compare("l"."UnitPrice", '50.0') >= 0 THEN 'Large Premium'
WHEN "l"."Quantity" > 100 THEN 'Large Standard'
WHEN ef_compare("l"."UnitPrice", '50.0') >= 0 THEN 'Small Premium'
ELSE 'Small Standard'
END AS "Tag"
FROM "LineItems" AS "l"SELECT l."Id", CASE
WHEN l."Quantity" > 100 AND l."UnitPrice" >= 50.0 THEN 'Large Premium'
WHEN l."Quantity" > 100 THEN 'Large Standard'
WHEN l."UnitPrice" >= 50.0 THEN 'Small Premium'
ELSE 'Small Standard'
END AS "Tag"
FROM "LineItems" AS lSELECT [l].[Id], CASE
WHEN [l].[Quantity] > 100 AND [l].[UnitPrice] >= 50.0 THEN N'Large Premium'
WHEN [l].[Quantity] > 100 THEN N'Large Standard'
WHEN [l].[UnitPrice] >= 50.0 THEN N'Small Premium'
ELSE N'Small Standard'
END AS [Tag]
FROM [LineItems] AS [l]playground.line_items.Aggregate([
{
"$project" : {
"_id" : "$_id",
"Tag" : {
"$cond" : {
"if" : {
"$and" : [
{
"$ne" : ["$$ROOT", null]
},
{
"$gt" : ["$Quantity", 100]
},
{
"$gte" : [
"$UnitPrice",
{ "$numberDecimal" : "50" }
]
}
]
},
"then" : "Large Premium",
"else" : {
"$cond" : {
"if" : {
"$and" : [
{
"$ne" : ["$$ROOT", null]
},
{
"$gt" : ["$Quantity", 100]
}
]
},
"then" : "Large Standard",
"else" : {
"$cond" : {
"if" : {
"$and" : [
{
"$ne" : ["$$ROOT", null]
},
{
"$gte" : [
"$UnitPrice",
{ "$numberDecimal" : "50" }
]
}
]
},
"then" : "Small Premium",
"else" : "Small Standard"
}
}
}
}
}
}
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_LineItemExt.ClassifyItem_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_LineItem.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_LineItemExt
{
// [Expressive]
// public static string ClassifyItem(this LineItem i) => i switch
// {
// { Quantity: > 100, UnitPrice: >= 50m } => "Large Premium",
// { Quantity: > 100 } => "Large Standard",
// { UnitPrice: >= 50m } => "Small Premium",
// _ => "Small Standard",
// };
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem, string>> ClassifyItem_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_LineItem_Expression()
{
var p_i = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem), "i");
var expr_0 = global::System.Linq.Expressions.Expression.Constant("Small Standard", typeof(string)); // "Small Standard"
var expr_2 = global::System.Linq.Expressions.Expression.Constant(null, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem));
var expr_1 = global::System.Linq.Expressions.Expression.NotEqual(p_i, expr_2);
var expr_3 = global::System.Linq.Expressions.Expression.Property(p_i, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem).GetProperty("UnitPrice", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
var expr_5 = global::System.Linq.Expressions.Expression.Constant(50m, typeof(decimal)); // 50m
var expr_4 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_3, expr_5);
var expr_6 = global::System.Linq.Expressions.Expression.AndAlso(expr_1, expr_4);
var expr_7 = global::System.Linq.Expressions.Expression.Constant("Small Premium", typeof(string)); // "Small Premium"
var expr_8 = global::System.Linq.Expressions.Expression.Condition(expr_6, expr_7, expr_0, typeof(string));
var expr_10 = global::System.Linq.Expressions.Expression.Constant(null, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem));
var expr_9 = global::System.Linq.Expressions.Expression.NotEqual(p_i, expr_10);
var expr_11 = global::System.Linq.Expressions.Expression.Property(p_i, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem).GetProperty("Quantity", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
var expr_13 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
var expr_12 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, expr_11, expr_13);
var expr_14 = global::System.Linq.Expressions.Expression.AndAlso(expr_9, expr_12);
var expr_15 = global::System.Linq.Expressions.Expression.Constant("Large Standard", typeof(string)); // "Large Standard"
var expr_16 = global::System.Linq.Expressions.Expression.Condition(expr_14, expr_15, expr_8, typeof(string));
var expr_18 = global::System.Linq.Expressions.Expression.Constant(null, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem));
var expr_17 = global::System.Linq.Expressions.Expression.NotEqual(p_i, expr_18);
var expr_19 = global::System.Linq.Expressions.Expression.Property(p_i, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem).GetProperty("Quantity", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
var expr_21 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
var expr_20 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, expr_19, expr_21);
var expr_22 = global::System.Linq.Expressions.Expression.Property(p_i, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem).GetProperty("UnitPrice", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
var expr_24 = global::System.Linq.Expressions.Expression.Constant(50m, typeof(decimal)); // 50m
var expr_23 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_22, expr_24);
var expr_25 = global::System.Linq.Expressions.Expression.AndAlso(expr_17, expr_20);
var expr_26 = global::System.Linq.Expressions.Expression.AndAlso(expr_25, expr_23);
var expr_27 = global::System.Linq.Expressions.Expression.Constant("Large Premium", typeof(string)); // "Large Premium"
var expr_28 = global::System.Linq.Expressions.Expression.Condition(expr_26, expr_27, expr_16, typeof(string));
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem, string>>(expr_28, p_i);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_LineItemExt.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_LineItemExt { }
}
// === 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.LineItemExt).GetMethod("ClassifyItem", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_LineItemExt", "ClassifyItem_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_LineItem_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, "geA7+xo3OGLKCksgqbWzpYIBAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<T1> __Polyfill_Select_3e61_14_24<T0, T1>(
this global::ExpressiveSharp.IExpressiveQueryable<T0> source,
global::System.Func<T0, T1> __func)
{
// Source: i => new { i.Id, Tag = i.ClassifyItem() }
var i3e6114c24_p_i = global::System.Linq.Expressions.Expression.Parameter(typeof(T0), "i");
var i3e6114c24_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6114c24_p_i, typeof(T0).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // i.Id
var i3e6114c24_expr_2 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.LineItemExt).GetMethod("ClassifyItem", 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[] { i3e6114c24_p_i }); // i.ClassifyItem()
var i3e6114c24_expr_3 = typeof(T1).GetConstructors()[0];
var i3e6114c24_expr_0 = global::System.Linq.Expressions.Expression.New(i3e6114c24_expr_3, new global::System.Linq.Expressions.Expression[] { i3e6114c24_expr_1, i3e6114c24_expr_2 }, new global::System.Reflection.MemberInfo[] { typeof(T1).GetProperty("Id"), typeof(T1).GetProperty("Tag") });
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<T0, T1>>(i3e6114c24_expr_0, i3e6114c24_p_i);
return (global::ExpressiveSharp.IExpressiveQueryable<T1>)(object)
global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<T0>)(object)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
.LineItems
.Select(i => new { i.Id, Tag = i.ClassifyItem() })
// Setup
public static class LineItemExt
{
[Expressive]
public static string ClassifyItem(this LineItem i) => i switch
{
{ Quantity: > 100, UnitPrice: >= 50m } => "Large Premium",
{ Quantity: > 100 } => "Large Standard",
{ UnitPrice: >= 50m } => "Small Premium",
_ => "Small Standard",
};
}Generated SQL:
SELECT "l"."Id", CASE
WHEN "l"."Quantity" > 100 AND ef_compare("l"."UnitPrice", '50.0') >= 0 THEN 'Large Premium'
WHEN "l"."Quantity" > 100 THEN 'Large Standard'
WHEN ef_compare("l"."UnitPrice", '50.0') >= 0 THEN 'Small Premium'
ELSE 'Small Standard'
END AS "Tag"
FROM "LineItems" AS "l"Pattern-to-Condition Cheat Sheet
All switch expressions map to conditional expressions in the target language (SQL CASE, MongoDB $switch, etc.). Here is a summary of how different patterns translate at the C# layer:
| C# Pattern | Generated Condition |
|---|---|
>= 100 | col >= 100 |
>= 80 and < 90 | col >= 80 && col < 90 |
1 or 2 | col == 1 || col == 2 |
"Premium" | col == "Premium" |
_ (discard) | fallback (else) branch |
> 50 when Flag | col > 50 && Flag |
Best Practices
Always include a discard arm (
_) to ensure the conditional has a fallback branch.Keep arms simple for translation. Each arm's pattern and result should be a simple expression. Avoid calling methods that cannot be translated by your provider.
Order arms from most specific to least specific, just as you would in C#. The generated ternary chain evaluates top-to-bottom, matching the provider's conditional evaluation order.
Prefer switch expressions over nested ternaries for readability. The source generator produces ternary chains regardless, but the switch expression in your source code is easier to read and maintain.
Use
[Expressive]methods for complex switches rather than inline switch expressions in queries:csharp// Prefer this: reusable and readable [Expressive] public string GetGrade() => Price switch { ... }; // Over this: inline in every query db.Orders.Select(o => o.Price switch { ... });
See also Pattern Matching for the full list of supported patterns.
