Skip to content

[ExpressiveFor] Mapping

The [ExpressiveFor] attribute lets you provide expression-tree bodies for members on types you do not own -- BCL methods, third-party library members, or your own types that cannot use [Expressive] directly. This enables those members to be used in EF Core queries and other LINQ providers that would otherwise fail with "could not be translated".

Namespace

csharp
using ExpressiveSharp.Mapping;

How It Works

You write a stub member -- a method or a property -- whose body defines the expression-tree replacement. The [ExpressiveFor] attribute tells the generator which external member this stub maps to. At runtime, the replacer substitutes calls to the target member with the stub's expression tree -- call sites remain unchanged.

Mapping Rules

  • The stub can be a method (receiver supplied as the first parameter for instance targets, or this for instance stubs on the target type) or a property (parameterless; this is the receiver for instance stubs).
  • The single-argument form [ExpressiveFor(nameof(X))] is shorthand for [ExpressiveFor(typeof(ContainingType), nameof(X))] -- use it when the target member is on the same type as the stub.
  • For static methods (and static stubs over static members), the stub's parameters must match the target method's parameters exactly.
  • For instance methods with a static stub, the first parameter of the stub is the receiver (this), followed by the target method's parameters.
  • For instance methods with an instance stub on the target type, this is the receiver; remaining parameters match the target's exactly.
  • For instance properties with a static method stub, the stub takes a single parameter: the receiver.
  • For instance properties with an instance method or property stub on the target type, the stub is parameterless.
  • For static properties, the stub is parameterless.
  • Property stubs can only target other properties (no parameters to carry method arguments).
  • The return type / property type must match (EXP0017 if not).
  • Constructor stubs ([ExpressiveForConstructor]) must still be static methods; instance or property ctor stubs have no coherent meaning.

Static Method Mapping

Map a static method by matching its parameter signature:

db
    .Orders
    .Where(o => System.Math.Clamp(o.Items.Count(), 0, 100) > 5)

// Setup
public static class MathMappings
{
    [ExpressiveSharp.Mapping.ExpressiveFor(typeof(System.Math), nameof(System.Math.Clamp))]
    public static int ClampInt(int value, int min, int max)
        => value < min ? min : (value > max ? max : value);
}
SELECT "o"."Id", "o"."CustomerId", "o"."PlacedAt", "o"."Status"
FROM "Orders" AS "o"
WHERE CASE
    WHEN (
        SELECT COUNT(*)
        FROM "LineItems" AS "l"
        WHERE "o"."Id" = "l"."OrderId") < 0 THEN 0
    WHEN (
        SELECT COUNT(*)
        FROM "LineItems" AS "l0"
        WHERE "o"."Id" = "l0"."OrderId") > 100 THEN 100
    ELSE (
        SELECT COUNT(*)
        FROM "LineItems" AS "l1"
        WHERE "o"."Id" = "l1"."OrderId")
END > 5
SELECT o."Id", o."CustomerId", o."PlacedAt", o."Status"
FROM "Orders" AS o
WHERE CASE
    WHEN (
        SELECT count(*)::int
        FROM "LineItems" AS l
        WHERE o."Id" = l."OrderId") < 0 THEN 0
    WHEN (
        SELECT count(*)::int
        FROM "LineItems" AS l0
        WHERE o."Id" = l0."OrderId") > 100 THEN 100
    ELSE (
        SELECT count(*)::int
        FROM "LineItems" AS l1
        WHERE o."Id" = l1."OrderId")
END > 5
SELECT [o].[Id], [o].[CustomerId], [o].[PlacedAt], [o].[Status]
FROM [Orders] AS [o]
WHERE CASE
    WHEN (
        SELECT COUNT(*)
        FROM [LineItems] AS [l]
        WHERE [o].[Id] = [l].[OrderId]) < 0 THEN 0
    WHEN (
        SELECT COUNT(*)
        FROM [LineItems] AS [l0]
        WHERE [o].[Id] = [l0].[OrderId]) > 100 THEN 100
    ELSE (
        SELECT COUNT(*)
        FROM [LineItems] AS [l1]
        WHERE [o].[Id] = [l1].[OrderId])
END > 5
playground.orders.Aggregate([
    {
         "$match" : {
             "$expr" : {
                 "$gt" : [
                    {
                         "$cond" : {
                             "if" : {
                                 "$lt" : [
                                    { "$size" : "$Items" },
                                    0
                                ] 
                            },
                            "then" : 0,
                            "else" : {
                                 "$cond" : {
                                     "if" : {
                                         "$gt" : [
                                            { "$size" : "$Items" },
                                            100
                                        ] 
                                    },
                                    "then" : 100,
                                    "else" : { "$size" : "$Items" } 
                                } 
                            } 
                        } 
                    },
                    5
                ] 
            } 
        } 
    }
])
// === System_Math.Clamp_P0_int_P1_int_P2_int.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 System;

namespace ExpressiveSharp.Generated
{
    static partial class System_Math 
    {
        // [ExpressiveSharp.Mapping.ExpressiveFor(typeof(System.Math), nameof(System.Math.Clamp))]
        // public static int ClampInt(int value, int min, int max) => value < min ? min : (value > max ? max : value);
        static global::System.Linq.Expressions.Expression<global::System.Func<int, int, int, int>> Clamp_P0_int_P1_int_P2_int_Expression() 
        {
            var p_value = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "value");
            var p_min = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "min");
            var p_max = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "max");
            var expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.LessThan, p_value, p_min); // value < min
            var expr_3 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, p_value, p_max); // value > max
            var expr_2 = global::System.Linq.Expressions.Expression.Condition(expr_3, p_max, p_value, typeof(int));
            var expr_0 = global::System.Linq.Expressions.Expression.Condition(expr_1, p_min, expr_2, typeof(int));
            return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<int, int, int, int>>(expr_0, p_value, p_min, p_max);
        }
    }
}


// === System_Math.Attributes.g.cs ===
// <auto-generated/>

namespace ExpressiveSharp.Generated
{
    [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
    static partial class System_Math { }
}


// === 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::System.Math).GetMethod("Clamp", allFlags, null, new global::System.Type[] { typeof(int), typeof(int), typeof(int) }, null), "ExpressiveSharp.Generated.System_Math", "Clamp_P0_int_P1_int_P2_int_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, "8YrIMIHO1qxL/V6nOcXllH8BAABfX1NuaXBwZXQuY3M=")]
        internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> __Polyfill_Where_3e61_14_21(
            this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
            global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, bool> __func)
        {
            // Source: o => System.Math.Clamp(o.Items.Count(), 0, 100) > 5
            var i3e6114c21_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
            var i3e6114c21_expr_3 = global::System.Linq.Expressions.Expression.Property(i3e6114c21_p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("Items", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Items
            var i3e6114c21_expr_2 = global::System.Linq.Expressions.Expression.Call(global::System.Linq.Enumerable.First(global::System.Linq.Enumerable.Where(typeof(global::System.Linq.Enumerable).GetMethods(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static), m => m.Name == "Count" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && !m.GetParameters()[0].ParameterType.IsGenericParameter)).MakeGenericMethod(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem)), new global::System.Linq.Expressions.Expression[] { i3e6114c21_expr_3 });
            var i3e6114c21_expr_4 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
            var i3e6114c21_expr_5 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
            var i3e6114c21_expr_1 = global::System.Linq.Expressions.Expression.Call(typeof(global::System.Math).GetMethod("Clamp", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(int), typeof(int), typeof(int) }, null), new global::System.Linq.Expressions.Expression[] { i3e6114c21_expr_2, i3e6114c21_expr_4, i3e6114c21_expr_5 });
            var i3e6114c21_expr_6 = global::System.Linq.Expressions.Expression.Constant(5, typeof(int)); // 5
            var i3e6114c21_expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, i3e6114c21_expr_1, i3e6114c21_expr_6);
            var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, bool>>(i3e6114c21_expr_0, i3e6114c21_p_o);
            return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
                global::System.Linq.Queryable.Where(
                    (global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order>)source,
                    __lambda));
        }
    }
}

namespace System.Runtime.CompilerServices
{
    [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)]
    file sealed class InterceptsLocationAttribute : global::System.Attribute
    {
        public InterceptsLocationAttribute(int version, string data) { }
    }
}

Instance Method Mapping

For instance methods, the first parameter represents the receiver:

db
    .Products
    .Where(p => p.Name.Contains("box"))

// Setup
public static class StringMappings
{
    [ExpressiveSharp.Mapping.ExpressiveFor(typeof(string), nameof(string.Contains))]
    public static bool Contains(string self, string value)
        => self.IndexOf(value) >= 0;
}
SELECT "p"."Id", "p"."Category", "p"."ListPrice", "p"."Name", "p"."StockQuantity"
FROM "Products" AS "p"
WHERE instr("p"."Name", 'box') - 1 >= 0
SELECT p."Id", p."Category", p."ListPrice", p."Name", p."StockQuantity"
FROM "Products" AS p
WHERE strpos(p."Name", 'box') - 1 >= 0
SELECT [p].[Id], [p].[Category], [p].[ListPrice], [p].[Name], [p].[StockQuantity]
FROM [Products] AS [p]
WHERE CAST(CHARINDEX(N'box', [p].[Name]) AS int) - 1 >= 0
playground.products.Aggregate([
    {
         "$match" : {
             "$expr" : {
                 "$gte" : [
                    {
                         "$indexOfCP" : ["$Name", "box"] 
                    },
                    0
                ] 
            } 
        } 
    }
])
// === System_String.Contains_P0_string.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 System;

namespace ExpressiveSharp.Generated
{
    static partial class System_String 
    {
        // [ExpressiveSharp.Mapping.ExpressiveFor(typeof(string), nameof(string.Contains))]
        // public static bool Contains(string self, string value) => self.IndexOf(value) >= 0;
        static global::System.Linq.Expressions.Expression<global::System.Func<string, string, bool>> Contains_P0_string_Expression() 
        {
            var p_self = global::System.Linq.Expressions.Expression.Parameter(typeof(string), "self");
            var p_value = global::System.Linq.Expressions.Expression.Parameter(typeof(string), "value");
            var expr_1 = global::System.Linq.Expressions.Expression.Call(p_self, typeof(string).GetMethod("IndexOf", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { typeof(string) }, null), new global::System.Linq.Expressions.Expression[] { p_value }); // self.IndexOf(value)
            var expr_2 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
            var expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_1, expr_2);
            return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<string, string, bool>>(expr_0, p_self, p_value);
        }
    }
}


// === System_String.Attributes.g.cs ===
// <auto-generated/>

namespace ExpressiveSharp.Generated
{
    [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
    static partial class System_String { }
}


// === 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(string).GetMethod("Contains", allFlags, null, new global::System.Type[] { typeof(string) }, null), "ExpressiveSharp.Generated.System_String", "Contains_P0_string_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, "6NDyQ3WFbY1mEQWbqHDkFIEBAABfX1NuaXBwZXQuY3M=")]
        internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product> __Polyfill_Where_3e61_14_23(
            this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product> source,
            global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product, bool> __func)
        {
            // Source: p => p.Name.Contains("box")
            var i3e6114c23_p_p = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product), "p");
            var i3e6114c23_expr_1 = global::System.Linq.Expressions.Expression.Constant("box", typeof(string)); // "box"
            var i3e6114c23_expr_2 = global::System.Linq.Expressions.Expression.Property(i3e6114c23_p_p, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // p.Name
            var i3e6114c23_expr_0 = global::System.Linq.Expressions.Expression.Call(i3e6114c23_expr_2, typeof(string).GetMethod("Contains", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { typeof(string) }, null), new global::System.Linq.Expressions.Expression[] { i3e6114c23_expr_1 });
            var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product, bool>>(i3e6114c23_expr_0, i3e6114c23_p_p);
            return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
                global::System.Linq.Queryable.Where(
                    (global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Product>)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) { }
    }
}

Instance Property Mapping

For instance properties, the stub takes a single parameter (the instance):

csharp
static class EntityMappings
{
    [ExpressiveFor(typeof(MyType), nameof(MyType.FullName))]
    static string FullName(MyType obj)
        => $"{obj.FirstName} {obj.LastName}";
}

Co-located Form (Instance Stub + Single-argument Attribute)

When the target is on the same type as the stub, the most ergonomic form combines an instance stub with the single-argument attribute. this is the receiver automatically. Use this form when a property has its own backing storage -- e.g. a plain settable auto-property used for DTO shape, serialization, or in-memory assignment in tests -- but queries should still compute it from other columns.

A property stub is often the cleanest choice for this (no parentheses, reads like the target it replaces):

csharp
public class Person
{
    public string FirstName { get; set; } = "";
    public string LastName { get; set; } = "";

    // Regular auto-property — assignable directly (for DTOs, tests, deserialization).
    public string FullName { get; set; } = "";

    // When FullName appears in a LINQ expression tree, it is rewritten to this body,
    // so EF Core projects it from FirstName/LastName instead of mapping it to its own column.
    [ExpressiveFor(nameof(FullName))]
    private string FullNameExpression => $"{FirstName} {LastName}";
}

A method stub is equivalent in behaviour and appropriate when the target is a method or when you need a block body:

csharp
[ExpressiveFor(nameof(FullName))]
private string FullNameExpression() => $"{FirstName} {LastName}";

Both forms are equivalent to the verbose [ExpressiveFor(typeof(Person), nameof(Person.FullName))] static string FullName(Person obj) => $"{obj.FirstName} {obj.LastName}"; but reuse this instead of threading a receiver parameter. When the EF Core integration is enabled, both the target property and the stub property itself are automatically excluded from the model (no [NotMapped] needed -- see Automatic NotMapped for [ExpressiveFor] targets).

When to prefer [Expressive] instead

If the property has no backing storage and the same body works at both runtime and query time, put [Expressive] directly on it ([Expressive] public string FullName => $"{FirstName} {LastName}";) and skip the stub. [ExpressiveFor] is for the dual-body case; [Expressive] is for the single-body case.

TIP

The stub can use any C# syntax that [Expressive] supports -- switch expressions, pattern matching, null-conditional operators, and more.

Automatic NotMapped for [ExpressiveFor] targets

When UseExpressives() is active, EF Core's model builder automatically ignores properties that are:

  1. Decorated with [Expressive],
  2. Decorated with [ExpressiveFor] (a property stub itself), or
  3. The target of an [ExpressiveFor] stub anywhere in the loaded assemblies.

You do not need to add [NotMapped] to a property you are expressing externally or using as a property stub -- the ExpressivePropertiesNotMappedConvention detects these cases via attribute metadata and the generated registry and calls Ignore() for you.

Constructor Mapping with [ExpressiveForConstructor]

Use [ExpressiveForConstructor] to provide an expression-tree body for a constructor on a type you do not own:

csharp
public static class MyDtoBuilder
{
    // Applied to a static stub method that returns the target type — the
    // generator replaces `new MyDto(id, name)` call sites with the stub's body.
    [ExpressiveForConstructor(typeof(MyDto))]
    public static MyDto Build(int id, string name)
        => new MyDto { Id = id, Name = name };
}

Synthesizing a new property

[ExpressiveFor] maps onto an existing member. If the target property does not yet exist and you want the generator to declare it for you (for example, so HotChocolate or EF Core projection middleware can bind to a settable member), use [ExpressiveProperty] instead — it's the focused attribute for that case.

Properties

Both [ExpressiveFor] and [ExpressiveForConstructor] support the same optional properties as [Expressive]:

PropertyTypeDefaultDescription
AllowBlockBodyboolfalseEnables block-bodied stubs (if/else, local variables, etc.)
TransformersType[]?nullPer-mapping transformers applied when expanding the mapped member
db
    .Orders
    .Where(o => System.Math.Clamp(o.Items.Count(), 0, 100) > 5)

// Setup
public static class MathBlockMappings
{
    [ExpressiveSharp.Mapping.ExpressiveFor(typeof(System.Math), nameof(System.Math.Clamp), AllowBlockBody = true)]
    public static int ClampInt(int value, int min, int max)
    {
        if (value < min) return min;
        if (value > max) return max;
        return value;
    }
}
SELECT "o"."Id", "o"."CustomerId", "o"."PlacedAt", "o"."Status"
FROM "Orders" AS "o"
WHERE CASE
    WHEN (
        SELECT COUNT(*)
        FROM "LineItems" AS "l"
        WHERE "o"."Id" = "l"."OrderId") < 0 THEN 0
    WHEN (
        SELECT COUNT(*)
        FROM "LineItems" AS "l0"
        WHERE "o"."Id" = "l0"."OrderId") > 100 THEN 100
    ELSE (
        SELECT COUNT(*)
        FROM "LineItems" AS "l1"
        WHERE "o"."Id" = "l1"."OrderId")
END > 5
SELECT o."Id", o."CustomerId", o."PlacedAt", o."Status"
FROM "Orders" AS o
WHERE CASE
    WHEN (
        SELECT count(*)::int
        FROM "LineItems" AS l
        WHERE o."Id" = l."OrderId") < 0 THEN 0
    WHEN (
        SELECT count(*)::int
        FROM "LineItems" AS l0
        WHERE o."Id" = l0."OrderId") > 100 THEN 100
    ELSE (
        SELECT count(*)::int
        FROM "LineItems" AS l1
        WHERE o."Id" = l1."OrderId")
END > 5
SELECT [o].[Id], [o].[CustomerId], [o].[PlacedAt], [o].[Status]
FROM [Orders] AS [o]
WHERE CASE
    WHEN (
        SELECT COUNT(*)
        FROM [LineItems] AS [l]
        WHERE [o].[Id] = [l].[OrderId]) < 0 THEN 0
    WHEN (
        SELECT COUNT(*)
        FROM [LineItems] AS [l0]
        WHERE [o].[Id] = [l0].[OrderId]) > 100 THEN 100
    ELSE (
        SELECT COUNT(*)
        FROM [LineItems] AS [l1]
        WHERE [o].[Id] = [l1].[OrderId])
END > 5
playground.orders.Aggregate([
    {
         "$match" : {
             "$expr" : {
                 "$gt" : [
                    {
                         "$cond" : {
                             "if" : {
                                 "$lt" : [
                                    { "$size" : "$Items" },
                                    0
                                ] 
                            },
                            "then" : 0,
                            "else" : {
                                 "$cond" : {
                                     "if" : {
                                         "$gt" : [
                                            { "$size" : "$Items" },
                                            100
                                        ] 
                                    },
                                    "then" : 100,
                                    "else" : { "$size" : "$Items" } 
                                } 
                            } 
                        } 
                    },
                    5
                ] 
            } 
        } 
    }
])
// === System_Math.Clamp_P0_int_P1_int_P2_int.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 System;

namespace ExpressiveSharp.Generated
{
    static partial class System_Math 
    {
        // [ExpressiveSharp.Mapping.ExpressiveFor(typeof(System.Math), nameof(System.Math.Clamp), AllowBlockBody = true)]
        // public static int ClampInt(int value, int min, int max)
        // {
        //     if (value < min)
        //         return min;
        //     if (value > max)
        //         return max;
        //     return value;
        // }
        static global::System.Linq.Expressions.Expression<global::System.Func<int, int, int, int>> Clamp_P0_int_P1_int_P2_int_Expression() 
        {
            var p_value = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "value");
            var p_min = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "min");
            var p_max = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "max");
            var expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.LessThan, p_value, p_min); // value < min
            var expr_3 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, p_value, p_max); // value > max
            var expr_2 = global::System.Linq.Expressions.Expression.Condition(expr_3, p_max, p_value, typeof(int));
            var expr_0 = global::System.Linq.Expressions.Expression.Condition(expr_1, p_min, expr_2, typeof(int));
            return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<int, int, int, int>>(expr_0, p_value, p_min, p_max);
        }
    }
}


// === System_Math.Attributes.g.cs ===
// <auto-generated/>

namespace ExpressiveSharp.Generated
{
    [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
    static partial class System_Math { }
}


// === 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::System.Math).GetMethod("Clamp", allFlags, null, new global::System.Type[] { typeof(int), typeof(int), typeof(int) }, null), "ExpressiveSharp.Generated.System_Math", "Clamp_P0_int_P1_int_P2_int_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, "UILC4uUSV9nrMPTmvdyTZH8BAABfX1NuaXBwZXQuY3M=")]
        internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> __Polyfill_Where_3e61_14_21(
            this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
            global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, bool> __func)
        {
            // Source: o => System.Math.Clamp(o.Items.Count(), 0, 100) > 5
            var i3e6114c21_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
            var i3e6114c21_expr_3 = global::System.Linq.Expressions.Expression.Property(i3e6114c21_p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("Items", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Items
            var i3e6114c21_expr_2 = global::System.Linq.Expressions.Expression.Call(global::System.Linq.Enumerable.First(global::System.Linq.Enumerable.Where(typeof(global::System.Linq.Enumerable).GetMethods(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static), m => m.Name == "Count" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && !m.GetParameters()[0].ParameterType.IsGenericParameter)).MakeGenericMethod(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem)), new global::System.Linq.Expressions.Expression[] { i3e6114c21_expr_3 });
            var i3e6114c21_expr_4 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
            var i3e6114c21_expr_5 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
            var i3e6114c21_expr_1 = global::System.Linq.Expressions.Expression.Call(typeof(global::System.Math).GetMethod("Clamp", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(int), typeof(int), typeof(int) }, null), new global::System.Linq.Expressions.Expression[] { i3e6114c21_expr_2, i3e6114c21_expr_4, i3e6114c21_expr_5 });
            var i3e6114c21_expr_6 = global::System.Linq.Expressions.Expression.Constant(5, typeof(int)); // 5
            var i3e6114c21_expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, i3e6114c21_expr_1, i3e6114c21_expr_6);
            var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, bool>>(i3e6114c21_expr_0, i3e6114c21_p_o);
            return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
                global::System.Linq.Queryable.Where(
                    (global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order>)source,
                    __lambda));
        }
    }
}

namespace System.Runtime.CompilerServices
{
    [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)]
    file sealed class InterceptsLocationAttribute : global::System.Attribute
    {
        public InterceptsLocationAttribute(int version, string data) { }
    }
}

Diagnostics

The following diagnostics are specific to [ExpressiveFor] and [ExpressiveForConstructor]:

CodeSeverityDescription
EXP0014ErrorTarget type specified in [ExpressiveFor] could not be resolved
EXP0015ErrorNo member with the given name found on the target type matching the stub's parameter signature
EXP0017ErrorReturn type of the stub does not match the target member's return type
EXP0019ErrorThe target member already has [Expressive] -- remove one of the two attributes
EXP0020ErrorDuplicate mapping -- only one stub per target member is allowed

WARNING

If a member already has [Expressive], adding [ExpressiveFor] targeting it is a compile error (EXP0019). [ExpressiveFor] is only for members that do not have [Expressive].

Complete Usage Example

db
    .Orders
    .Where(o => !string.IsNullOrWhiteSpace(o.Customer.Name))
    .Where(o => System.Math.Clamp(o.Items.Count(), 0, 100) > 5)
    .Select(o => new OrderMappingDto(o.Id, o.Customer.Name ?? "N/A"))

// Setup
public static class MathMappingsComplete
{
    [ExpressiveSharp.Mapping.ExpressiveFor(typeof(System.Math), nameof(System.Math.Clamp))]
    public static int ClampInt(int value, int min, int max)
        => value < min ? min : (value > max ? max : value);
}

public static class StringMappingsComplete
{
    [ExpressiveSharp.Mapping.ExpressiveFor(typeof(string), nameof(string.IsNullOrWhiteSpace))]
    public static bool IsNullOrWhiteSpace(string? s)
        => s == null || s.Trim().Length == 0;
}

public class OrderMappingDto
{
    public int Id { get; set; }
    public string Name { get; set; } = "";

    // The constructor that call sites (new OrderMappingDto(id, name)) invoke.
    public OrderMappingDto(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

public static class OrderMappingDtoBuilder
{
    // Provides a translatable body for the constructor above — call sites
    // `new OrderMappingDto(id, name)` are rewritten to this object-init form
    // during expression-tree expansion, so the provider sees a translatable
    // MemberInit instead of a constructor call.
    [ExpressiveSharp.Mapping.ExpressiveForConstructor(typeof(OrderMappingDto))]
    public static OrderMappingDto Build(int id, string name)
        => new OrderMappingDto(0, "") { Id = id, Name = name };
}
SELECT "o"."Id", "c"."Name"
FROM "Orders" AS "o"
INNER JOIN "Customers" AS "c" ON "o"."CustomerId" = "c"."Id"
WHERE length(trim("c"."Name")) <> 0 AND CASE
    WHEN (
        SELECT COUNT(*)
        FROM "LineItems" AS "l"
        WHERE "o"."Id" = "l"."OrderId") < 0 THEN 0
    WHEN (
        SELECT COUNT(*)
        FROM "LineItems" AS "l0"
        WHERE "o"."Id" = "l0"."OrderId") > 100 THEN 100
    ELSE (
        SELECT COUNT(*)
        FROM "LineItems" AS "l1"
        WHERE "o"."Id" = "l1"."OrderId")
END > 5
SELECT o."Id", c."Name"
FROM "Orders" AS o
INNER JOIN "Customers" AS c ON o."CustomerId" = c."Id"
WHERE length(btrim(c."Name", E' \t\n\r'))::int <> 0 AND CASE
    WHEN (
        SELECT count(*)::int
        FROM "LineItems" AS l
        WHERE o."Id" = l."OrderId") < 0 THEN 0
    WHEN (
        SELECT count(*)::int
        FROM "LineItems" AS l0
        WHERE o."Id" = l0."OrderId") > 100 THEN 100
    ELSE (
        SELECT count(*)::int
        FROM "LineItems" AS l1
        WHERE o."Id" = l1."OrderId")
END > 5
SELECT [o].[Id], [c].[Name]
FROM [Orders] AS [o]
INNER JOIN [Customers] AS [c] ON [o].[CustomerId] = [c].[Id]
WHERE CAST(LEN(LTRIM(RTRIM([c].[Name]))) AS int) <> 0 AND CASE
    WHEN (
        SELECT COUNT(*)
        FROM [LineItems] AS [l]
        WHERE [o].[Id] = [l].[OrderId]) < 0 THEN 0
    WHEN (
        SELECT COUNT(*)
        FROM [LineItems] AS [l0]
        WHERE [o].[Id] = [l0].[OrderId]) > 100 THEN 100
    ELSE (
        SELECT COUNT(*)
        FROM [LineItems] AS [l1]
        WHERE [o].[Id] = [l1].[OrderId])
END > 5
System.InvalidOperationException: Duplicate element name '_id'.
   at MongoDB.Bson.BsonDocument.Add(BsonElement element)
   at MongoDB.Bson.BsonDocument.AddRange(IEnumerable`1 elements)
   at MongoDB.Bson.BsonDocument..ctor(IEnumerable`1 elements)
   at MongoDB.Driver.Linq.Linq3Implementation.Ast.Stages.AstProjectStage.Render()
   at MongoDB.Driver.Linq.Linq3Implementation.Ast.AstPipeline.<>c.<Render>b__12_0(AstStage s)
   at System.Linq.Enumerable.IListSelectIterator`2.MoveNext()
   at MongoDB.Bson.BsonArray.AddRange(IEnumerable`1 values)
   at MongoDB.Bson.BsonArray..ctor(IEnumerable`1 values)
   at MongoDB.Driver.Linq.Linq3Implementation.Ast.AstPipeline.Render()
   at MongoDB.Driver.Linq.Linq3Implementation.Ast.AstNode.ToString()
   at System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted[T](T value)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators.ExecutableQuery`3.ToString()
   at MongoDB.Driver.Linq.Linq3Implementation.MongoQuery`2.ToString()
// === System_Math.Clamp_P0_int_P1_int_P2_int.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 System;

namespace ExpressiveSharp.Generated
{
    static partial class System_Math 
    {
        // [ExpressiveSharp.Mapping.ExpressiveFor(typeof(System.Math), nameof(System.Math.Clamp))]
        // public static int ClampInt(int value, int min, int max) => value < min ? min : (value > max ? max : value);
        static global::System.Linq.Expressions.Expression<global::System.Func<int, int, int, int>> Clamp_P0_int_P1_int_P2_int_Expression() 
        {
            var p_value = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "value");
            var p_min = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "min");
            var p_max = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "max");
            var expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.LessThan, p_value, p_min); // value < min
            var expr_3 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, p_value, p_max); // value > max
            var expr_2 = global::System.Linq.Expressions.Expression.Condition(expr_3, p_max, p_value, typeof(int));
            var expr_0 = global::System.Linq.Expressions.Expression.Condition(expr_1, p_min, expr_2, typeof(int));
            return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<int, int, int, int>>(expr_0, p_value, p_min, p_max);
        }
    }
}


// === System_String.IsNullOrWhiteSpace_P0_string.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 System;

namespace ExpressiveSharp.Generated
{
    static partial class System_String 
    {
        // [ExpressiveSharp.Mapping.ExpressiveFor(typeof(string), nameof(string.IsNullOrWhiteSpace))]
        // public static bool IsNullOrWhiteSpace(string? s) => s == null || s.Trim().Length == 0;
        static global::System.Linq.Expressions.Expression<global::System.Func<string, bool>> IsNullOrWhiteSpace_P0_string_Expression() 
        {
            var p_s = global::System.Linq.Expressions.Expression.Parameter(typeof(string), "s");
            var expr_3 = global::System.Linq.Expressions.Expression.Constant(null, typeof(object)); // null
            var expr_2 = global::System.Linq.Expressions.Expression.Convert(expr_3, typeof(string));
            var expr_1 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Equal, p_s, expr_2);
            var expr_6 = global::System.Linq.Expressions.Expression.Call(p_s, typeof(string).GetMethod("Trim", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] {  }, null), global::System.Array.Empty<global::System.Linq.Expressions.Expression>()); // s.Trim()
            var expr_5 = global::System.Linq.Expressions.Expression.Property(expr_6, typeof(string).GetProperty("Length", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
            var expr_7 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
            var expr_4 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Equal, expr_5, expr_7);
            var expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.OrElse, expr_1, expr_4);
            return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<string, bool>>(expr_0, p_s);
        }
    }
}


// === ExpressiveSharp_Docs_Playground_Snippet_OrderMappingDto._ctor_P0_int_P1_string.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_OrderMappingDto 
    {
        // // Provides a translatable body for the constructor above — call sites
        // // `new OrderMappingDto(id, name)` are rewritten to this object-init form
        // // during expression-tree expansion, so the provider sees a translatable
        // // MemberInit instead of a constructor call.
        // [ExpressiveSharp.Mapping.ExpressiveForConstructor(typeof(OrderMappingDto))]
        // public static OrderMappingDto Build(int id, string name) => new OrderMappingDto(0, "")
        // {
        //     Id = id,
        //     Name = name
        // };
        static global::System.Linq.Expressions.Expression<global::System.Func<int, string, global::ExpressiveSharp.Docs.Playground.Snippet.OrderMappingDto>> _ctor_P0_int_P1_string_Expression() 
        {
            var p_id = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "id");
            var p_name = global::System.Linq.Expressions.Expression.Parameter(typeof(string), "name");
            var expr_1 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
            var expr_2 = global::System.Linq.Expressions.Expression.Constant("", typeof(string)); // ""
            var expr_3 = global::System.Linq.Expressions.Expression.New(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderMappingDto).GetConstructor(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { typeof(int), typeof(string) }, null), expr_1, expr_2);
            var expr_4 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderMappingDto).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), p_id);
            var expr_5 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderMappingDto).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), p_name);
            var expr_0 = global::System.Linq.Expressions.Expression.MemberInit(expr_3, expr_4, expr_5);
            return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<int, string, global::ExpressiveSharp.Docs.Playground.Snippet.OrderMappingDto>>(expr_0, p_id, p_name);
        }
    }
}


// === System_Math.Attributes.g.cs ===
// <auto-generated/>

namespace ExpressiveSharp.Generated
{
    [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
    static partial class System_Math { }
}


// === System_String.Attributes.g.cs ===
// <auto-generated/>

namespace ExpressiveSharp.Generated
{
    [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
    static partial class System_String { }
}


// === ExpressiveSharp_Docs_Playground_Snippet_OrderMappingDto.Attributes.g.cs ===
// <auto-generated/>

namespace ExpressiveSharp.Generated
{
    [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
    static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderMappingDto { }
}


// === 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::System.Math).GetMethod("Clamp", allFlags, null, new global::System.Type[] { typeof(int), typeof(int), typeof(int) }, null), "ExpressiveSharp.Generated.System_Math", "Clamp_P0_int_P1_int_P2_int_Expression");
            Register(map, typeof(string).GetMethod("IsNullOrWhiteSpace", allFlags, null, new global::System.Type[] { typeof(string) }, null), "ExpressiveSharp.Generated.System_String", "IsNullOrWhiteSpace_P0_string_Expression");
            Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderMappingDto).GetConstructor(allFlags, null, new global::System.Type[] { typeof(int), typeof(string) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderMappingDto", "_ctor_P0_int_P1_string_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, "RItiCGs1JyldsO31flGkeQECAABfX1NuaXBwZXQuY3M=")]
        internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.Playground.Snippet.OrderMappingDto> __Polyfill_Select_3e61_17_5(
            this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
            global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, global::ExpressiveSharp.Docs.Playground.Snippet.OrderMappingDto> __func)
        {
            // Source: o => new OrderMappingDto(o.Id, o.Customer.Name ?? "N/A")
            var i3e6117c5_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
            var i3e6117c5_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6117c5_p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Id
            var i3e6117c5_expr_4 = global::System.Linq.Expressions.Expression.Property(i3e6117c5_p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("Customer", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Customer
            var i3e6117c5_expr_3 = global::System.Linq.Expressions.Expression.Property(i3e6117c5_expr_4, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
            var i3e6117c5_expr_5 = global::System.Linq.Expressions.Expression.Constant("N/A", typeof(string)); // "N/A"
            var i3e6117c5_expr_2 = global::System.Linq.Expressions.Expression.Coalesce(i3e6117c5_expr_3, i3e6117c5_expr_5);
            var i3e6117c5_expr_0 = global::System.Linq.Expressions.Expression.New(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderMappingDto).GetConstructor(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { typeof(int), typeof(string) }, null), i3e6117c5_expr_1, i3e6117c5_expr_2);
            var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, global::ExpressiveSharp.Docs.Playground.Snippet.OrderMappingDto>>(i3e6117c5_expr_0, i3e6117c5_p_o);
            return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
                global::System.Linq.Queryable.Select(
                    (global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order>)source,
                    __lambda));
        }
        [global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "RItiCGs1JyldsO31flGkecEBAABfX1NuaXBwZXQuY3M=")]
        internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> __Polyfill_Where_3e61_16_5(
            this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
            global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, bool> __func)
        {
            // Source: o => System.Math.Clamp(o.Items.Count(), 0, 100) > 5
            var i3e6116c5_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
            var i3e6116c5_expr_3 = global::System.Linq.Expressions.Expression.Property(i3e6116c5_p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("Items", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Items
            var i3e6116c5_expr_2 = global::System.Linq.Expressions.Expression.Call(global::System.Linq.Enumerable.First(global::System.Linq.Enumerable.Where(typeof(global::System.Linq.Enumerable).GetMethods(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static), m => m.Name == "Count" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && !m.GetParameters()[0].ParameterType.IsGenericParameter)).MakeGenericMethod(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem)), new global::System.Linq.Expressions.Expression[] { i3e6116c5_expr_3 });
            var i3e6116c5_expr_4 = global::System.Linq.Expressions.Expression.Constant(0, typeof(int)); // 0
            var i3e6116c5_expr_5 = global::System.Linq.Expressions.Expression.Constant(100, typeof(int)); // 100
            var i3e6116c5_expr_1 = global::System.Linq.Expressions.Expression.Call(typeof(global::System.Math).GetMethod("Clamp", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(int), typeof(int), typeof(int) }, null), new global::System.Linq.Expressions.Expression[] { i3e6116c5_expr_2, i3e6116c5_expr_4, i3e6116c5_expr_5 });
            var i3e6116c5_expr_6 = global::System.Linq.Expressions.Expression.Constant(5, typeof(int)); // 5
            var i3e6116c5_expr_0 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThan, i3e6116c5_expr_1, i3e6116c5_expr_6);
            var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, bool>>(i3e6116c5_expr_0, i3e6116c5_p_o);
            return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
                global::System.Linq.Queryable.Where(
                    (global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order>)source,
                    __lambda));
        }
        [global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "RItiCGs1JyldsO31flGkeYQBAABfX1NuaXBwZXQuY3M=")]
        internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> __Polyfill_Where_3e61_15_5(
            this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
            global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, bool> __func)
        {
            // Source: o => !string.IsNullOrWhiteSpace(o.Customer.Name)
            var i3e6115c5_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
            var i3e6115c5_expr_3 = global::System.Linq.Expressions.Expression.Property(i3e6115c5_p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("Customer", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Customer
            var i3e6115c5_expr_2 = global::System.Linq.Expressions.Expression.Property(i3e6115c5_expr_3, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance));
            var i3e6115c5_expr_1 = global::System.Linq.Expressions.Expression.Call(typeof(string).GetMethod("IsNullOrWhiteSpace", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(string) }, null), new global::System.Linq.Expressions.Expression[] { i3e6115c5_expr_2 });
            var i3e6115c5_expr_0 = global::System.Linq.Expressions.Expression.MakeUnary(global::System.Linq.Expressions.ExpressionType.Not, i3e6115c5_expr_1, typeof(bool));
            var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, bool>>(i3e6115c5_expr_0, i3e6115c5_p_o);
            return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
                global::System.Linq.Queryable.Where(
                    (global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order>)source,
                    __lambda));
        }
    }
}

namespace System.Runtime.CompilerServices
{
    [global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)]
    file sealed class InterceptsLocationAttribute : global::System.Attribute
    {
        public InterceptsLocationAttribute(int version, string data) { }
    }
}

All three mapped members are replaced with their expression-tree equivalents and translated for your provider. No changes are needed at call sites.

Released under the MIT License.