Quick Start
This guide walks you through a complete end-to-end example — from installing the NuGet packages to seeing the translated output for your provider.
Prerequisites
- .NET 8 SDK or later (.NET 10 also supported)
- A LINQ provider. ExpressiveSharp integrates with EF Core, MongoDB, or any
IQueryable<T>.
Step 1 — Install the Packages
Install the core package first:
dotnet add package ExpressiveSharpThen pick the integration that matches your data source:
dotnet add package ExpressiveSharp.EntityFrameworkCoredotnet add package ExpressiveSharp.MongoDB# Nothing else — call .AsExpressive() on your IQueryable<T>| Package | Purpose |
|---|---|
ExpressiveSharp | Core runtime — expression expansion, transformers, IExpressiveQueryable<T>, ExpressionPolyfill (includes Abstractions) |
ExpressiveSharp.Abstractions | Lightweight — [Expressive] attribute, [ExpressiveFor], IExpressionTreeTransformer, source generator only (no runtime services) |
ExpressiveSharp.EntityFrameworkCore | EF Core integration — UseExpressives(), ExpressiveDbSet<T>, Include/ThenInclude, async methods, analyzers and code fixes |
ExpressiveSharp.MongoDB | MongoDB integration — .AsExpressive() on IMongoCollection<T>, MQL aggregation translation |
ExpressiveSharp.EntityFrameworkCore.RelationalExtensions | SQL window functions — ranking (ROW_NUMBER, RANK, DENSE_RANK, NTILE, PERCENT_RANK, CUME_DIST), aggregate (SUM, AVG, COUNT, MIN, MAX), and navigation (LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE) with PARTITION BY / ORDER BY / ROWS|RANGE frame support, plus indexed Select. |
Step 2 — Define Your Entities
Add [Expressive] to any property or method whose body you want translated into an expression tree:
using ExpressiveSharp;
public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string? Email { get; set; }
public ICollection<Order> Orders { get; set; } = new List<Order>();
// Computed property — reusable in any query, translated for any provider
[Expressive]
public bool IsVip => Orders.Count() > 10;
}
public class Order
{
public int Id { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; } = null!;
[Expressive]
public decimal Total => Price * Quantity;
// Switch expression — normally illegal in expression trees
[Expressive]
public string Grade => Price switch
{
>= 100 => "Premium",
>= 50 => "Standard",
_ => "Budget",
};
}The source generator runs at compile time and emits a companion Expression<TDelegate> for each [Expressive] member — no runtime reflection.
Step 3 — Wire Up Your Provider
using ExpressiveSharp.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
public class AppDbContext : DbContext
{
// ExpressiveDbSet<T> lets modern C# syntax flow through DbSet chains
public ExpressiveDbSet<Customer> Customers => this.ExpressiveSet<Customer>();
public ExpressiveDbSet<Order> Orders => this.ExpressiveSet<Order>();
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite("Data Source=app.db")
.UseExpressives(); // register [Expressive] expansion
}using ExpressiveSharp.MongoDB.Extensions;
using MongoDB.Driver;
var db = new MongoClient("mongodb://localhost:27017").GetDatabase("shop");
var customers = db.GetCollection<Customer>("customers").AsExpressive();
var orders = db.GetCollection<Order>("orders").AsExpressive();using ExpressiveSharp;
// Any IQueryable<T> — your own provider, LINQ to Objects, etc.
IQueryable<Customer> raw = GetCustomers();
var customers = raw.AsExpressive();Step 4 — Write Modern-Syntax Queries
Modern C# syntax — null-conditional operators, switch expressions, pattern matching, and [Expressive] member access — all work directly in the query:
db
.Orders
.Where(o => o.Customer.Email != null && o.Total() > 50)
.Select(o => new { o.Id, Total = o.Total(), Grade = o.Grade(), Email = o.Customer.Email })
.OrderByDescending(x => x.Total)
.Take(10)
// Setup
public static class OrderExt
{
// Computed sum of line items â reusable in any query, translated to SQL/MQL
[Expressive]
public static decimal Total(this Order o) => o.Items.Sum(i => i.UnitPrice * i.Quantity);
// Switch expression over the computed total â illegal in raw expression trees,
// but [Expressive] expands it into a provider-translatable tree.
[Expressive]
public static string Grade(this Order o) => o.Total() switch
{
>= 100m => "Premium",
>= 50m => "Standard",
_ => "Budget",
};
}.param set @p 10
SELECT "o"."Id", (
SELECT COALESCE(ef_sum(ef_multiply("l1"."UnitPrice", CAST("l1"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l1"
WHERE "o"."Id" = "l1"."OrderId") AS "Total", CASE
WHEN ef_compare((
SELECT COALESCE(ef_sum(ef_multiply("l2"."UnitPrice", CAST("l2"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l2"
WHERE "o"."Id" = "l2"."OrderId"), '100.0') >= 0 THEN 'Premium'
WHEN ef_compare((
SELECT COALESCE(ef_sum(ef_multiply("l3"."UnitPrice", CAST("l3"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l3"
WHERE "o"."Id" = "l3"."OrderId"), '50.0') >= 0 THEN 'Standard'
ELSE 'Budget'
END AS "Grade", "c"."Email"
FROM "Orders" AS "o"
INNER JOIN "Customers" AS "c" ON "o"."CustomerId" = "c"."Id"
WHERE "c"."Email" IS NOT NULL AND ef_compare((
SELECT COALESCE(ef_sum(ef_multiply("l"."UnitPrice", CAST("l"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l"
WHERE "o"."Id" = "l"."OrderId"), '50.0') > 0
ORDER BY (
SELECT COALESCE(ef_sum(ef_multiply("l0"."UnitPrice", CAST("l0"."Quantity" AS TEXT))), '0.0')
FROM "LineItems" AS "l0"
WHERE "o"."Id" = "l0"."OrderId") COLLATE EF_DECIMAL DESC
LIMIT @pThe tabs above show how this exact query translates for each provider. The ?. operator, the [Expressive] Total and Grade members, and the switch expression inside Grade are all compiled into the provider's native query language — no data is loaded into memory for filtering or projection.
Step 5 — Inspect the Generated Query
// Use ToQueryString() to inspect the SQL without executing
var sql = ctx.Orders
.Where(o => o.Customer.Email != null)
.Select(o => new { o.Id, o.Grade })
.ToQueryString();
Console.WriteLine(sql);// ToString() on the queryable yields the aggregation pipeline
var pipeline = orders
.Where(o => o.Customer.Email != null)
.Select(o => new { o.Id, o.Grade })
.ToString();
Console.WriteLine(pipeline);Next Steps
- IExpressiveQueryable<T> — the core provider-agnostic API
- [Expressive] Properties — computed properties in depth
- [Expressive] Methods — parameterized query fragments
- Constructor Projections — project DTOs directly in queries
- EF Core Integration — full EF Core setup
- MongoDB Integration — full MongoDB setup
