@@ -56,30 +56,53 @@ features:
5656
5757## At a Glance
5858
59+ ** Without Projectables** — the same sub-expression copy-pasted into every query:
60+
61+ ``` csharp
62+ // ❌ Repeated 4× in a single query — change the formula and hunt down every copy
63+ var orders = dbContext .Orders
64+ .Where (o => o .Lines .Sum (l => l .Quantity * l .UnitPrice ) > 500 )
65+ .OrderByDescending (o => o .Lines .Sum (l => l .Quantity * l .UnitPrice ) * (1 + o .TaxRate ))
66+ .Select (o => new
67+ {
68+ Total = o .Lines .Sum (l => l .Quantity * l .UnitPrice ) * (1 + o .TaxRate ),
69+ Tier = o .Lines .Sum (l => l .Quantity * l .UnitPrice ) > 1000 ? " Premium" : " Standard"
70+ })
71+ .ToList ();
72+ ```
73+
74+ ** With Projectables** — define once on the entity, compose freely, use anywhere:
75+
5976``` csharp
77+ // ✅ Business logic lives on the entity — queries stay clean
6078class Order
6179{
6280 public decimal TaxRate { get ; set ; }
63- public ICollection <OrderItem > Items { get ; set ; }
81+ public ICollection <OrderLine > Lines { get ; set ; }
82+
83+ [Projectable ]
84+ public decimal Subtotal => Lines .Sum (l => l .Quantity * l .UnitPrice );
6485
65- [Projectable ]
66- public decimal Subtotal => Items .Sum (item => item .Product .ListPrice * item .Quantity );
67-
6886 [Projectable ]
69- public decimal Tax => Subtotal * TaxRate ;
70-
87+ public decimal Total => Subtotal * ( 1 + TaxRate ); // composes ↑
88+
7189 [Projectable ]
72- public decimal GrandTotal => Subtotal + Tax ;
90+ public string Tier => Subtotal switch // pattern matching → SQL CASE
91+ {
92+ > 1000 => " Premium" ,
93+ > 250 => " Standard" ,
94+ _ => " Basic"
95+ };
7396}
7497
75- // Use it anywhere in your queries — translated to SQL automatically
76- var result = dbContext . Users
77- .Where ( u => u . UserName == " Jon " )
78- .Select (u => new { u . GetMostRecentOrder (). GrandTotal })
79- .FirstOrDefault ();
98+ var orders = dbContext . Orders
99+ . Where ( o => o . Subtotal > 500 ) // → WHERE
100+ .OrderByDescending ( o => o . Total ) // → ORDER BY
101+ .Select (o => new { o . Total , o . Tier }) // → SELECT
102+ .ToList ();
80103```
81104
82- The properties are ** inlined into the SQL** — no client-side evaluation, no N+1.
105+ The properties are ** inlined into SQL at query time ** — no client-side evaluation, no N+1, no duplicate expressions .
83106
84107## NuGet Packages
85108
0 commit comments