Constructor Projections
Mark a constructor with [Expressive] to project your DTOs directly inside LINQ queries. The generator emits a MemberInit expression (new T() { Prop = value, ... }) that your LINQ provider can translate to a native projection.
Why Constructor Projections?
Without constructor projections, you must write inline anonymous types or repeat object-initializer logic in every query:
db
.Orders
.Select(o => new OrderSummaryDto
{
Id = o.Id,
Description = $"Order #{o.Id}",
Total = o.Items.Sum(i => i.UnitPrice * i.Quantity),
})
// Setup
public class OrderSummaryDto
{
public int Id { get; set; }
public string Description { get; set; } = "";
public decimal Total { get; set; }
}SELECT "o"."Id", 'Order #' || CAST("o"."Id" AS TEXT) AS "Description", (
SELECT COALESCE(ef_sum(ef_multiply("l"."UnitPrice", CAST("l"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId") AS "Total"
FROM "Orders" AS "o"SELECT o."Id", 'Order #' || o."Id"::text AS "Description", (
SELECT COALESCE(sum(l."UnitPrice" * l."Quantity"::numeric(18,2)), 0.0)
FROM "LineItems" AS l
WHERE o."Id" = l."OrderId") AS "Total"
FROM "Orders" AS oSELECT [o].[Id], N'Order #' + CONVERT(varchar(11), [o].[Id]) AS [Description], (
SELECT COALESCE(SUM([l].[UnitPrice] * CAST([l].[Quantity] AS decimal(18,2))), 0.0)
FROM [LineItems] AS [l]
WHERE [o].[Id] = [l].[OrderId]) AS [Total]
FROM [Orders] AS [o]playground.orders.Aggregate([
{
"$project" : {
"_id" : "$_id",
"Description" : {
"$concat" : [
"Order #",
{ "$toString" : "$_id" }
]
},
"Total" : {
"$sum" : {
"$map" : {
"input" : "$Items",
"as" : "i",
"in" : {
"$multiply" : ["$$i.UnitPrice", "$$i.Quantity"]
}
}
}
}
}
}
])// === PolyfillInterceptors_b1293e61.g.cs ===
// <auto-generated/>
#nullable disable
namespace ExpressiveSharp.Generated.Interceptors
{
internal static partial class PolyfillInterceptors
{
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "qUsNY3N7K/6XRy0rptbmtYQBAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto> __Polyfill_Select_3e61_15_5(
this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto> __func)
{
// Source: o => new OrderSummaryDto { Id = o.Id, Description = $"Order #{o.Id}", Total = o.Items.Sum(i => i.UnitPrice * i.Quantity), }
var i3e6115c5_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
var i3e6115c5_expr_1 = global::System.Linq.Expressions.Expression.New(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetConstructor(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { }, null)); // new OrderSummaryDto { Id = o.Id, Desc...
var i3e6115c5_expr_2 = global::System.Linq.Expressions.Expression.Property(i3e6115c5_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 i3e6115c5_expr_3 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), i3e6115c5_expr_2);
var i3e6115c5_expr_4 = global::System.Linq.Expressions.Expression.Constant("Order #", typeof(string)); // $"Order #{o.Id}"
var i3e6115c5_expr_5 = global::System.Linq.Expressions.Expression.Property(i3e6115c5_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 i3e6115c5_expr_6 = global::System.Linq.Expressions.Expression.Call(i3e6115c5_expr_5, typeof(int).GetMethod("ToString", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { }, null));
var i3e6115c5_expr_7 = global::System.Linq.Expressions.Expression.Call(typeof(string).GetMethod("Concat", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(string), typeof(string) }, null), i3e6115c5_expr_4, i3e6115c5_expr_6);
var i3e6115c5_expr_8 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetProperty("Description", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), i3e6115c5_expr_7);
var i3e6115c5_expr_10 = global::System.Linq.Expressions.Expression.Property(i3e6115c5_p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("Items", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Items
var p_i_11 = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem), "i"); // i => i.UnitPrice * i.Quantity
var i3e6115c5_expr_13 = global::System.Linq.Expressions.Expression.Property(p_i_11, 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 i3e6115c5_expr_15 = global::System.Linq.Expressions.Expression.Property(p_i_11, 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 i3e6115c5_expr_14 = global::System.Linq.Expressions.Expression.Convert(i3e6115c5_expr_15, typeof(decimal));
var i3e6115c5_expr_12 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, i3e6115c5_expr_13, i3e6115c5_expr_14);
var i3e6115c5_expr_16 = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem, decimal>>(i3e6115c5_expr_12, p_i_11);
var i3e6115c5_expr_9 = global::System.Linq.Expressions.Expression.Call(global::System.Linq.Enumerable.First(global::System.Linq.Enumerable.Where(typeof(global::System.Linq.Enumerable).GetMethods(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static), m => m.Name == "Sum" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType.IsGenericType && !m.GetParameters()[0].ParameterType.IsGenericParameter && m.GetParameters()[1].ParameterType.IsGenericType && !m.GetParameters()[1].ParameterType.IsGenericParameter && m.GetParameters()[1].ParameterType.GetGenericArguments()[1] == typeof(decimal))).MakeGenericMethod(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem)), new global::System.Linq.Expressions.Expression[] { i3e6115c5_expr_10, i3e6115c5_expr_16 });
var i3e6115c5_expr_17 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetProperty("Total", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), i3e6115c5_expr_9);
var i3e6115c5_expr_0 = global::System.Linq.Expressions.Expression.MemberInit(i3e6115c5_expr_1, i3e6115c5_expr_3, i3e6115c5_expr_8, i3e6115c5_expr_17);
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto>>(i3e6115c5_expr_0, i3e6115c5_p_o);
return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order>)source,
__lambda));
}
}
}
namespace System.Runtime.CompilerServices
{
[global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : global::System.Attribute
{
public InterceptsLocationAttribute(int version, string data) { }
}
}db
.Orders
.Select(o => new OrderSummaryDto
{
Id = o.Id,
Description = $"Order #{o.Id}",
Total = o.Items.Sum(i => i.UnitPrice * i.Quantity),
})
// Setup
public class OrderSummaryDto
{
public int Id { get; set; }
public string Description { get; set; } = "";
public decimal Total { get; set; }
}Generated SQL:
SELECT "o"."Id", 'Order #' || CAST("o"."Id" AS TEXT) AS "Description", (
SELECT COALESCE(ef_sum(ef_multiply("l"."UnitPrice", CAST("l"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId") AS "Total"
FROM "Orders" AS "o"With an [Expressive] constructor, you define the projection once and use it everywhere:
db
.Orders
.Select(o => new OrderSummaryDto(o.Id, $"Order #{o.Id}", o.Total()))
// Setup
public class OrderSummaryDto
{
public int Id { get; set; }
public string Description { get; set; } = "";
public decimal Total { get; set; }
public OrderSummaryDto() { }
[Expressive]
public OrderSummaryDto(int id, string description, decimal total)
{
Id = id;
Description = description;
Total = total;
}
}
public static class OrderExt
{
[Expressive]
public static decimal Total(this Order o)
=> o.Items.Sum(i => i.UnitPrice * i.Quantity);
}SELECT "o"."Id", 'Order #' || CAST("o"."Id" AS TEXT) AS "Description", (
SELECT COALESCE(ef_sum(ef_multiply("l"."UnitPrice", CAST("l"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId") AS "Total"
FROM "Orders" AS "o"SELECT o."Id", 'Order #' || o."Id"::text AS "Description", (
SELECT COALESCE(sum(l."UnitPrice" * l."Quantity"::numeric(18,2)), 0.0)
FROM "LineItems" AS l
WHERE o."Id" = l."OrderId") AS "Total"
FROM "Orders" AS oSELECT [o].[Id], N'Order #' + CONVERT(varchar(11), [o].[Id]) AS [Description], (
SELECT COALESCE(SUM([l].[UnitPrice] * CAST([l].[Quantity] AS decimal(18,2))), 0.0)
FROM [LineItems] AS [l]
WHERE [o].[Id] = [l].[OrderId]) AS [Total]
FROM [Orders] AS [o]playground.orders.Aggregate([
{
"$project" : {
"_id" : "$_id",
"Description" : {
"$concat" : [
"Order #",
{ "$toString" : "$_id" }
]
},
"Total" : {
"$sum" : {
"$map" : {
"input" : "$Items",
"as" : "i",
"in" : {
"$multiply" : ["$$i.UnitPrice", "$$i.Quantity"]
}
}
}
}
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_OrderSummaryDto._ctor_P0_int_P1_string_P2_decimal.g.cs ===
// <auto-generated/>
#nullable disable
using System;
using System.Linq;
using System.Linq.Expressions;
using ExpressiveSharp;
using ExpressiveSharp.EntityFrameworkCore;
using ExpressiveSharp.Docs.PlaygroundModel.Webshop;
using ExpressiveSharp.Docs.Playground.Snippet;
namespace ExpressiveSharp.Generated
{
static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderSummaryDto
{
// [Expressive]
// public OrderSummaryDto(int id, string description, decimal total)
// {
// Id = id;
// Description = description;
// Total = total;
// }
static global::System.Linq.Expressions.Expression<global::System.Func<int, string, decimal, global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto>> _ctor_P0_int_P1_string_P2_decimal_Expression()
{
var p_id = global::System.Linq.Expressions.Expression.Parameter(typeof(int), "id");
var p_description = global::System.Linq.Expressions.Expression.Parameter(typeof(string), "description");
var p_total = global::System.Linq.Expressions.Expression.Parameter(typeof(decimal), "total");
var expr_0 = global::System.Linq.Expressions.Expression.New(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetConstructor(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { }, null));
var expr_1 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), p_id);
var expr_2 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetProperty("Description", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), p_description);
var expr_3 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetProperty("Total", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), p_total);
var expr_4 = global::System.Linq.Expressions.Expression.MemberInit(expr_0, expr_1, expr_2, expr_3);
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<int, string, decimal, global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto>>(expr_4, p_id, p_description, p_total);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderExt.Total_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order.g.cs ===
// <auto-generated/>
#nullable disable
using System;
using System.Linq;
using System.Linq.Expressions;
using ExpressiveSharp;
using ExpressiveSharp.EntityFrameworkCore;
using ExpressiveSharp.Docs.PlaygroundModel.Webshop;
using ExpressiveSharp.Docs.Playground.Snippet;
namespace ExpressiveSharp.Generated
{
static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderExt
{
// [Expressive]
// public static decimal Total(this Order o) => o.Items.Sum(i => i.UnitPrice * i.Quantity);
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, decimal>> Total_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression()
{
var p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
var expr_1 = global::System.Linq.Expressions.Expression.Property(p_o, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order).GetProperty("Items", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // o.Items
var p_i_2 = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem), "i"); // i => i.UnitPrice * i.Quantity
var expr_4 = global::System.Linq.Expressions.Expression.Property(p_i_2, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem).GetProperty("UnitPrice", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // i.UnitPrice
var expr_6 = global::System.Linq.Expressions.Expression.Property(p_i_2, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem).GetProperty("Quantity", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // i.Quantity
var expr_5 = global::System.Linq.Expressions.Expression.Convert(expr_6, typeof(decimal));
var expr_3 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.Multiply, expr_4, expr_5);
var expr_7 = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem, decimal>>(expr_3, p_i_2);
var expr_0 = global::System.Linq.Expressions.Expression.Call(global::System.Linq.Enumerable.First(global::System.Linq.Enumerable.Where(typeof(global::System.Linq.Enumerable).GetMethods(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static), m => m.Name == "Sum" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType.IsGenericType && !m.GetParameters()[0].ParameterType.IsGenericParameter && m.GetParameters()[1].ParameterType.IsGenericType && !m.GetParameters()[1].ParameterType.IsGenericParameter && m.GetParameters()[1].ParameterType.GetGenericArguments()[1] == typeof(decimal))).MakeGenericMethod(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.LineItem)), new global::System.Linq.Expressions.Expression[] { expr_1, expr_7 });
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, decimal>>(expr_0, p_o);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_OrderSummaryDto.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_OrderSummaryDto { }
}
// === 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.OrderSummaryDto).GetConstructor(allFlags, null, new global::System.Type[] { typeof(int), typeof(string), typeof(decimal) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderSummaryDto", "_ctor_P0_int_P1_string_P2_decimal_Expression");
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderExt).GetMethod("Total", allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_OrderExt", "Total_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Order_Expression");
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, "SD5GwIsXxLHe2o+lD0/a238BAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto> __Polyfill_Select_3e61_14_21(
this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order> source,
global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto> __func)
{
// Source: o => new OrderSummaryDto(o.Id, $"Order #{o.Id}", o.Total())
var i3e6114c21_p_o = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order), "o");
var i3e6114c21_expr_1 = global::System.Linq.Expressions.Expression.Property(i3e6114c21_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 i3e6114c21_expr_2 = global::System.Linq.Expressions.Expression.Constant("Order #", typeof(string)); // $"Order #{o.Id}"
var i3e6114c21_expr_3 = global::System.Linq.Expressions.Expression.Property(i3e6114c21_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 i3e6114c21_expr_4 = global::System.Linq.Expressions.Expression.Call(i3e6114c21_expr_3, typeof(int).GetMethod("ToString", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { }, null));
var i3e6114c21_expr_5 = global::System.Linq.Expressions.Expression.Call(typeof(string).GetMethod("Concat", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(string), typeof(string) }, null), i3e6114c21_expr_2, i3e6114c21_expr_4);
var i3e6114c21_expr_6 = global::System.Linq.Expressions.Expression.Call(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderExt).GetMethod("Total", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Static, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order) }, null), new global::System.Linq.Expressions.Expression[] { i3e6114c21_p_o }); // o.Total()
var i3e6114c21_expr_0 = global::System.Linq.Expressions.Expression.New(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto).GetConstructor(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { typeof(int), typeof(string), typeof(decimal) }, null), i3e6114c21_expr_1, i3e6114c21_expr_5, i3e6114c21_expr_6);
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order, global::ExpressiveSharp.Docs.Playground.Snippet.OrderSummaryDto>>(i3e6114c21_expr_0, i3e6114c21_p_o);
return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Order>)source,
__lambda));
}
}
}
namespace System.Runtime.CompilerServices
{
[global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)]
file sealed class InterceptsLocationAttribute : global::System.Attribute
{
public InterceptsLocationAttribute(int version, string data) { }
}
}db
.Orders
.Select(o => new OrderSummaryDto(o.Id, $"Order #{o.Id}", o.Total()))
// Setup
public class OrderSummaryDto
{
public int Id { get; set; }
public string Description { get; set; } = "";
public decimal Total { get; set; }
public OrderSummaryDto() { }
[Expressive]
public OrderSummaryDto(int id, string description, decimal total)
{
Id = id;
Description = description;
Total = total;
}
}
public static class OrderExt
{
[Expressive]
public static decimal Total(this Order o)
=> o.Items.Sum(i => i.UnitPrice * i.Quantity);
}Generated SQL:
SELECT "o"."Id", 'Order #' || CAST("o"."Id" AS TEXT) AS "Description", (
SELECT COALESCE(ef_sum(ef_multiply("l"."UnitPrice", CAST("l"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId") AS "Total"
FROM "Orders" AS "o"Basic Example
public class OrderSummaryDto
{
public int Id { get; set; }
public string Description { get; set; } = "";
public decimal Total { get; set; }
public OrderSummaryDto() { } // required parameterless constructor
[Expressive]
public OrderSummaryDto(int id, string description, decimal total)
{
Id = id;
Description = description;
Total = total;
}
}The generator produces an expression equivalent to:
(int id, string description, decimal total) => new OrderSummaryDto()
{
Id = id,
Description = description,
Total = total
}Use it in a query -- the query tabs above show how each provider translates the projection.
TIP
o.Total() is itself an [Expressive] extension -- it is expanded recursively into o.Items.Sum(i => i.UnitPrice * i.Quantity) before translation. Constructor projections compose with expressive properties and methods seamlessly.
Requirements
The class must expose an accessible parameterless constructor (public, internal, or protected-internal). The generated code uses new T() { ... } (object-initializer syntax), which requires a parameterless constructor.
If the parameterless constructor is missing, the generator reports EXP0002.
Supported Constructs
Constructor bodies support the following constructs:
| Construct | Notes |
|---|---|
| Simple property assignments | Id = id; Description = description; |
| Local variable declarations | Inlined at each usage point |
if/else chains | Converted to ternary expressions / provider CASE |
| Switch expressions | Translated to nested ternary / CASE |
base()/this() initializer chains | Recursively inlines the delegated constructor's assignments |
Inheritance -- Base/This Initializer Chains
The generator recursively inlines delegated constructor assignments. This is useful with DTO inheritance hierarchies:
db
.Customers
.Select(c => new CustomerDetailDto(c))
// Setup
public class CustomerDto
{
public int Id { get; set; }
public string Name { get; set; } = "";
public CustomerDto() { }
[Expressive]
public CustomerDto(Customer c)
{
Id = c.Id;
Name = c.Name;
}
}
public class CustomerDetailDto : CustomerDto
{
public string? Email { get; set; }
public string Tier { get; set; } = "";
public CustomerDetailDto() { }
[Expressive]
public CustomerDetailDto(Customer c) : base(c)
{
Email = c.Email;
Tier = c.Orders.Count() >= 10 ? "Gold" : "Standard";
}
}SELECT "c"."Email", CASE
WHEN (
SELECT COUNT(*)
FROM "Orders" AS "o"
WHERE "c"."Id" = "o"."CustomerId") >= 10 THEN 'Gold'
ELSE 'Standard'
END AS "Tier"
FROM "Customers" AS "c"SELECT c."Email", CASE
WHEN (
SELECT count(*)::int
FROM "Orders" AS o
WHERE c."Id" = o."CustomerId") >= 10 THEN 'Gold'
ELSE 'Standard'
END AS "Tier"
FROM "Customers" AS cSELECT [c].[Email], CASE
WHEN (
SELECT COUNT(*)
FROM [Orders] AS [o]
WHERE [c].[Id] = [o].[CustomerId]) >= 10 THEN N'Gold'
ELSE N'Standard'
END AS [Tier]
FROM [Customers] AS [c]playground.customers.Aggregate([
{
"$project" : {
"Email" : "$Email",
"Tier" : {
"$cond" : {
"if" : {
"$gte" : [
{ "$size" : "$Orders" },
10
]
},
"then" : "Gold",
"else" : "Standard"
}
},
"_id" : 0
}
}
])// === ExpressiveSharp_Docs_Playground_Snippet_CustomerDto._ctor_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Customer.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_CustomerDto
{
// [Expressive]
// public CustomerDto(Customer c)
// {
// Id = c.Id;
// Name = c.Name;
// }
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer, global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDto>> _ctor_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Customer_Expression()
{
var p_c = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer), "c");
var expr_0 = global::System.Linq.Expressions.Expression.New(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDto).GetConstructor(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { }, null));
var expr_1 = global::System.Linq.Expressions.Expression.Property(p_c, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // c.Id
var expr_2 = global::System.Linq.Expressions.Expression.Property(p_c, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // c.Name
var expr_3 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDto).GetProperty("Id", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), expr_1);
var expr_4 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDto).GetProperty("Name", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), expr_2);
var expr_5 = global::System.Linq.Expressions.Expression.MemberInit(expr_0, expr_3, expr_4);
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer, global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDto>>(expr_5, p_c);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_CustomerDetailDto._ctor_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Customer.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_CustomerDetailDto
{
// [Expressive]
// public CustomerDetailDto(Customer c) : base(c)
// {
// Email = c.Email;
// Tier = c.Orders.Count() >= 10 ? "Gold" : "Standard";
// }
static global::System.Linq.Expressions.Expression<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer, global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDetailDto>> _ctor_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Customer_Expression()
{
var p_c = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer), "c");
var expr_0 = global::System.Linq.Expressions.Expression.New(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDetailDto).GetConstructor(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { }, null));
var expr_1 = global::System.Linq.Expressions.Expression.Property(p_c, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Email", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // c.Email
var expr_5 = global::System.Linq.Expressions.Expression.Property(p_c, typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer).GetProperty("Orders", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance)); // c.Orders
var expr_4 = 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.Order)), new global::System.Linq.Expressions.Expression[] { expr_5 });
var expr_6 = global::System.Linq.Expressions.Expression.Constant(10, typeof(int)); // 10
var expr_3 = global::System.Linq.Expressions.Expression.MakeBinary(global::System.Linq.Expressions.ExpressionType.GreaterThanOrEqual, expr_4, expr_6);
var expr_7 = global::System.Linq.Expressions.Expression.Constant("Gold", typeof(string)); // "Gold"
var expr_8 = global::System.Linq.Expressions.Expression.Constant("Standard", typeof(string)); // "Standard"
var expr_2 = global::System.Linq.Expressions.Expression.Condition(expr_3, expr_7, expr_8, typeof(string));
var expr_9 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDetailDto).GetProperty("Email", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), expr_1);
var expr_10 = global::System.Linq.Expressions.Expression.Bind(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDetailDto).GetProperty("Tier", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance), expr_2);
var expr_11 = global::System.Linq.Expressions.Expression.MemberInit(expr_0, expr_9, expr_10);
return global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer, global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDetailDto>>(expr_11, p_c);
}
}
}
// === ExpressiveSharp_Docs_Playground_Snippet_CustomerDto.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_CustomerDto { }
}
// === ExpressiveSharp_Docs_Playground_Snippet_CustomerDetailDto.Attributes.g.cs ===
// <auto-generated/>
namespace ExpressiveSharp.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static partial class ExpressiveSharp_Docs_Playground_Snippet_CustomerDetailDto { }
}
// === 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.CustomerDto).GetConstructor(allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_CustomerDto", "_ctor_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Customer_Expression");
Register(map, typeof(global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDetailDto).GetConstructor(allFlags, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer) }, null), "ExpressiveSharp.Generated.ExpressiveSharp_Docs_Playground_Snippet_CustomerDetailDto", "_ctor_P0_ExpressiveSharp_Docs_PlaygroundModel_Webshop_Customer_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, "ZR/mGl+fdK/+9lBu5lPkI4IBAABfX1NuaXBwZXQuY3M=")]
internal static global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDetailDto> __Polyfill_Select_3e61_14_24(
this global::ExpressiveSharp.IExpressiveQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer> source,
global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer, global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDetailDto> __func)
{
// Source: c => new CustomerDetailDto(c)
var i3e6114c24_p_c = global::System.Linq.Expressions.Expression.Parameter(typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer), "c");
var i3e6114c24_expr_0 = global::System.Linq.Expressions.Expression.New(typeof(global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDetailDto).GetConstructor(global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { typeof(global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer) }, null), i3e6114c24_p_c); // new CustomerDetailDto(c)
var __lambda = global::System.Linq.Expressions.Expression.Lambda<global::System.Func<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer, global::ExpressiveSharp.Docs.Playground.Snippet.CustomerDetailDto>>(i3e6114c24_expr_0, i3e6114c24_p_c);
return global::ExpressiveSharp.ExpressiveQueryableExtensions.AsExpressive(
global::System.Linq.Queryable.Select(
(global::System.Linq.IQueryable<global::ExpressiveSharp.Docs.PlaygroundModel.Webshop.Customer>)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
.Customers
.Select(c => new CustomerDetailDto(c))
// Setup
public class CustomerDto
{
public int Id { get; set; }
public string Name { get; set; } = "";
public CustomerDto() { }
[Expressive]
public CustomerDto(Customer c)
{
Id = c.Id;
Name = c.Name;
}
}
public class CustomerDetailDto : CustomerDto
{
public string? Email { get; set; }
public string Tier { get; set; } = "";
public CustomerDetailDto() { }
[Expressive]
public CustomerDetailDto(Customer c) : base(c)
{
Email = c.Email;
Tier = c.Orders.Count() >= 10 ? "Gold" : "Standard";
}
}Generated SQL:
SELECT "c"."Email", CASE
WHEN (
SELECT COUNT(*)
FROM "Orders" AS "o"
WHERE "c"."Id" = "o"."CustomerId") >= 10 THEN 'Gold'
ELSE 'Standard'
END AS "Tier"
FROM "Customers" AS "c"The generated expression inlines both the base constructor and the derived constructor body:
(Customer c) => new CustomerDetailDto()
{
Id = c.Id,
Name = c.Name,
Email = c.Email,
Tier = c.Orders.Count() >= 10 ? "Gold" : "Standard"
}Constructor Overloads
Multiple [Expressive] constructors per class are supported -- each overload generates its own expression, distinguished by parameter types:
public class OrderDto
{
public int Id { get; set; }
public decimal Total { get; set; }
public string? Note { get; set; }
public OrderDto() { }
[Expressive]
public OrderDto(int id, decimal total)
{
Id = id;
Total = total;
}
[Expressive]
public OrderDto(int id, decimal total, string note)
{
Id = id;
Total = total;
Note = note;
}
}Factory Method Conversion
If you have an existing [Expressive] factory method that returns new T { ... }, the generator emits diagnostic EXP0012 (Info severity) suggesting a conversion to a constructor.
Diagnostics
| Code | Severity | Description |
|---|---|---|
| EXP0002 | Error | Class is missing a parameterless constructor |
| EXP0003 | Error | Delegated constructor source not available (base/this chain cannot be analyzed) |
| EXP0012 | Info | Factory method can be converted to an [Expressive] constructor |
Next Steps
- [Expressive] Properties -- computed properties on entities
- [Expressive] Methods -- parameterized query fragments
- EF Core Integration -- full EF Core setup and features
