KitSysCo.BuildingBlocks.CommonLinq 1.0.1

BuildingBlocks.CommonLinq

CI Release

Build .NET LINQ expression trees from serializable, language-neutral contracts.

BuildingBlocks.CommonLinq lets a remote client (gRPC, REST, a JS/PHP frontend, a reporting engine…) describe a LINQ expression — filters, projections, conditionals, arithmetic, aggregations, method chains — as plain data, and compiles that description on the server into a real System.Linq.Expressions.Expression tree that can be executed against IQueryable, EF Core, or compiled delegates.

The contract is defined once in Protocol Buffers (common-linq.dto.proto) so any language that speaks protobuf can produce expressions.

Packages

Package Contents
KitSysCo.BuildingBlocks.CommonLinq Core: expression contract (protobuf DTOs), build pipeline, operators, decimal/Math/string/DateTime functions, LINQ method calls, projections, dataset query definitions
KitSysCo.BuildingBlocks.CommonLinq.SqlServer SQL Server add-on: EF.Functions.* function-call builders (DateDiff, DateFromParts, PatIndex, FreeText, …), SQL aggregate ModelBuilder mappings (STDEV, STDEVP, VAR, VARP, CHECKSUM_AGG)
dotnet add package KitSysCo.BuildingBlocks.CommonLinq
# optional, for SQL Server / EF.Functions support:
dotnet add package KitSysCo.BuildingBlocks.CommonLinq.SqlServer

Why

Typical use cases:

  • Dynamic reporting — let users define report columns, computed fields, and filters at runtime (see ideas-for-common-linq-storm.example.js for a realistic aging-report payload).
  • Server-driven querying — a frontend sends a filter/sort/project definition; the server turns it into a safe, parameterized EF Core query.
  • Cross-language rules — the same expression DTO can be produced by C#, PHP, or JS clients over gRPC.

Instead of parsing strings or building a DSL interpreter, you deserialize CommonLinqExpression and call BuildExpressionAsync — the output is a genuine expression tree with correct types, nullability, and provider translation (SQL).

How it works

CommonLinqExpression (DTO, protobuf oneof)
        │
        ▼  BuildExpressionAsync(context, ct)
ICommonLinqExpressionBuilderContext
  ├─ parameters / constants / scoped lambda params
  ├─ ICommonLinqExpressionBuilder (member access, constants parsing)
  └─ pluggable ICommonLinqExpressionBuilderService<T> (from DI)
        │   conditional · operations · function calls · init-object · lambda · method calls
        ▼
System.Linq.Expressions.Expression  ──►  EF Core / IQueryable / compiled delegate

Each node kind is handled by a small builder service resolved from DI (CanBuildExpression → first match wins), so the pipeline is fully extensible: register your own service and new node kinds or operators light up.

Requirements

  • .NET 10 (net10.0)
  • For SQL Server translation (EF.Functions.*, STDEV/VAR aggregates): EF Core + SQL Server, via the KitSysCo.BuildingBlocks.CommonLinq.SqlServer add-on package

Getting started

1. Register the builders

services.AddCommonLinqExpression();

// optional — SQL Server EF.Functions builders (from the SqlServer add-on package):
services.AddCommonLinqSqlServerExpression();

This registers all built-in builder services (see Core/Services/CommonLinqDIExtensions.cs).

2. Create a build context

The context carries the parameters and constants your expression can reference by name:

using System.Linq.Expressions;
using BuildingBlocks.CommonLinq.Contracts;
using BuildingBlocks.CommonLinq.Core.Services;

var x = Expression.Parameter(typeof(TransactionEntry), "x");

var context = new CommonLinqExpressionBuilderContext(
    services: serviceProvider,
    parametersExpression: new() { ["x"] = x },
    constantsExpression: new() { ["fromDate"] = Expression.Constant(DateTime.UtcNow) },
    ruleBuilder: null,                  // null → default builder (member access, constants, ...)
    restrictTargetOutputType: null);    // e.g. typeof(decimal) to force numeric alignment

3. Build an expression

From a DTO you received (or constructed):

// x.Amount >= 100.5m
var filter = new CommonLinqExpression
{
    ExprRl = new CommonLinqLeftRightRules
    {
        Op = ">=",
        Left  = new CommonLinqExpression { ExprParam   = "x.Amount" },
        Right = new CommonLinqExpression { ExprDecimal = "100.5" },
    }
};

Expression? body = await filter.BuildExpressionAsync(context, cancellationToken);

var lambda = Expression.Lambda<Func<TransactionEntry, bool>>((Expression)body!, x);
var matches = dbContext.TransactionEntries.Where(lambda).ToList();

You can also assemble DTOs fluently in C# with the static factory CommonLinqExpressionBuilder:

// x.PartnerCode ?? x.Transaction.PartnerCode
var coalesce = CommonLinqExpressionBuilder.CreateRuleSetOperationForExprParams(
    "??", ["x.PartnerCode", "x.Transaction.PartnerCode"]);

// select new TProject { ... } with every settable property mapped to the same-named member
var projection = CommonLinqExpressionBuilder
    .CreateEntityProjectExpression<TransactionEntry, TransactionEntryDto>("x");

4. What a client sends (JSON shape)

The same contract as JSON (e.g. from a JS frontend over gRPC/REST):

{
  "exprRuleSet": {
    "op": "&&",
    "rules": [
      { "exprParam": "x.Transaction.DueDate.HasValue" },
      {
        "exprRL": {
          "op": "<=",
          "left":  { "exprParam": "x.Transaction.DueDate.Value" },
          "right": { "exprParam": "fromDate" }
        }
      }
    ]
  }
}

The expression contract

CommonLinqExpression is a protobuf oneof — every node is one of:

Case DTO Meaning
expr_param string Shorthand member/parameter access: "x.PartnerId", "fromDate", statics like "DateTime.Today", "Math.PI"
expr_rule / expr_rule_set / expr_rules2..7 op + 1..7 operands N-ary operations (&&, ||, !, ??, arithmetic)
expr_rl op + left + right Binary operations (==, <, +, *, …)
expr_cond branches + default Conditional / ternary chains (when/then … else)
expr_func_call func name + args Static function calls (decimal.Round, Math.Max, string.Concat, EF.Functions.DateDiffDay, …)
expr_method_call target + method + args Instance/chain calls: Where, Any, Sum, FirstOrDefault, …
expr_lambda params + body Lambda, built as a method-call argument
expr_init_obj clrType / isRuntime + rulesMap Projection new T { A = …, B = … }, or a runtime-emitted type
expr_custom_rule / expr_custom_rule_any op + value (+ type) Extension point for your own rule kinds
expr_bool … expr_guid typed constants bool, numerics, decimal/char/string/DateTime/DateTimeOffset/TimeSpan/Guid (as strings)
expr_*_value wrapped constants Nullable variants of all of the above

Supported operators

Category Operators
Arithmetic + - * / ^
Logical && / AND, || / OR, ! / NOT
Comparison == (EQ, EQUAL, EQUALS), != (NE, <>), > / GT, >= / GTE, < / LT, <= / LTE
Set membership IN, NIN
Coalesce ??
Aggregates (method calls) SUM AVG COUNT MAX MIN, plus SQL STDEV STDEVP VAR VARP (SQL translation needs the SqlServer add-on)
LINQ methods Where, Any, First/FirstOrDefault, Last/LastOrDefault

Supported functions (expr_func_call)

Names are case-insensitive; str. is an alias for string. and EF functions accept short or fully-qualified names (DateDiffDay, EF.Functions.DateDiffDay, Microsoft.EntityFrameworkCore.EF.Functions.DateDiffDay).

  • decimal. — Round, Floor, Ceiling, Truncate, Add, Subtract, Multiply, Divide, Remainder, Negate
  • Math. — Abs, Acos, Asin, Atan, Ceiling, Cos, Cosh, Exp, Floor, Log, Max, Min, Pow, Round, Sign, Sin, Sinh, Sqrt, Tan, Tanh, Truncate
  • string. / str. — Concat, Join, IsNullOrEmpty, IsNullOrWhiteSpace, Trim, TrimStart, TrimEnd
  • DateTime. — DaysInMonth, IsLeapYear
  • EF.Functions. (SqlServer add-on package) — DateDiffYear … DateDiffNanosecond, DateFromParts, DateTimeFromParts, SmallDateTimeFromParts, DateTimeOffsetFromParts, TimeFromParts, IsDate, IsNumeric, PatIndex, Contains, FreeText

Dataset query definitions

Beyond single expressions, the proto defines composable query pipelines — CommonLinqQueryDatasetDefinition with an ordered list of transforms:

  • filter — where clause (a CommonLinqExpression)
  • sort — fields/expressions, ascending/descending
  • project — init-object projection
  • group — keys + aggregates (SUM, AVG, …) + optional having
  • join — Inner / Left / Right / Full, from another source/dataset
  • page — skip / take
  • distinct, union, unwind

Datasets can chain into each other via CommonLinqQueryDatasetDefinitionSource (from_source, from_dataset, or an embedded Any payload), and a CommonLinqQueryDatasetOptions selects the output dataset/transform.

Transforms are executed by pluggable ICommonLinqQueryDatasetTransformHandler implementations: each handler picks the transforms it supports (CanHandle) and folds them one by one into a running CommonLinqQueryDatasetResult (current lambda, body, and element type), which can be compiled to a delegate at the end.

SQL Server support (add-on package)

SQL Server-specific builders live in the separate KitSysCo.BuildingBlocks.CommonLinq.SqlServer package, so the core stays provider-agnostic. It registers the EF.Functions.* builders (DateDiff*, DateFromParts, PatIndex, IsDate, IsNumeric, Contains, FreeText):

services.AddCommonLinqExpression();
services.AddCommonLinqSqlServerExpression();

It also maps the built-in SQL aggregate functions so STDEV, STDEVP, VAR, VARP, and CHECKSUM_AGG translate to SQL — register them on your model:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.AddCommonLinqSupportedSqlEfFunctions();
}

Extensibility

Everything is interface-driven and resolved from DI — add your own registrations next to AddCommonLinqExpression():

  • ICommonLinqExpressionCustomRuleBuilder / ICommonLinqExpressionCustomRuleBuilderService — handle your own expr_custom_rule ops
  • ICommonLinqExpressionOperationRulesBuilder, ICommonLinqFunctionCallExpressionBuilderService, ICommonLinqMethodCallExpressionBuilderService, … — add operators, functions, or method-call kinds
  • ICommonLinqTypeResolver — control how clrType names resolve to Types (default: assembly-qualified/full/simple name across loaded assemblies)
  • ICommonLinqRuntimeTypeGenerator — control how isRuntime projections materialize a type (default: emits and caches a runtime class via Reflection.Emit, falling back to Dictionary<string, object?>)
  • ICommonLinqQueryDatasetTransformHandler — implement dataset-transform execution (filter/sort/group/join/…) for your own query pipeline
  • Satellite packages — the BuildingBlocks.CommonLinq.SqlServer project is the reference example of shipping provider-specific builders as an add-on package

Project layout

src/BuildingBlocks.CommonLinq/            Core package
  Abstractions/    Interfaces & base classes (builder, context, per-kind builder services, type resolvers, transform handlers)
  Contracts/       Protobuf-generated DTOs + partial classes containing the build dispatch
  Core/Services/   Builder service implementations, DI registration
  common-linq.dto.proto   The language-neutral contract (C# + PHP namespaces configured)
  CommonLinqExpressionBuilder.cs   Static C# factory helpers for assembling DTOs
src/BuildingBlocks.CommonLinq.SqlServer/  SQL Server add-on package (EF.Functions builders, ModelBuilder mappings)
ideas-for-common-linq-storm.example.js    End-to-end JSON example (aging report)

CI/CD & releasing

Two GitHub Actions workflows live in .github/workflows/:

  • CI (ci.yml) — on every push/PR to main: restore → build (Release) → pack, and uploads the .nupkg as a build artifact. Restore uses nuget.config, which lists nuget.org plus the private KitSys feed (anonymous read, no secrets needed).
  • Release (release.yml) — packs versioned packages and publishes them.

To cut a release, push a version tag (or run the workflow manually with a version input):

git tag v1.2.3
git push origin v1.2.3

The workflow strips the leading v, builds and packs both packages (KitSysCo.BuildingBlocks.CommonLinq and KitSysCo.BuildingBlocks.CommonLinq.SqlServer) with that version, pushes them, and creates a GitHub Release with the .nupkg files attached. The SqlServer package's dependency on the core package automatically gets the same version.

One-time setup in the repo settings:

  • Secret NUGET_API_KEY — API key for the push target (required).
  • Variable NUGET_PUSH_SOURCE — push endpoint; defaults to https://api.nuget.org/v3/index.json. Set it to https://kitsys-nuget.tvps.kitsysco.com/v3/index.json to publish to the private KitSys feed instead.

The <Version> in the .csproj is only a fallback for local dotnet pack; CI always overrides it from the tag.

License

Apache License 2.0 — © 2026 KitSysCo. See NOTICE.

Showing the top 20 packages that depend on KitSysCo.BuildingBlocks.CommonLinq.

Packages Downloads
KitSysCo.BuildingBlocks.CommonLinq.GrpcServer
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
17
KitSysCo.BuildingBlocks.CommonLinq.SqlServer
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
15
KitSysCo.BuildingBlocks.CommonLinq.GrpcClient
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
13
KitSysCo.BuildingBlocks.CommonLinq.SqlServer
SQL Server support for KitSysCo.BuildingBlocks.CommonLinq: EF Core SQL Server function-call builders (DateDiff, DateFromParts, PatIndex, IsDate, IsNumeric, FreeText, Contains), SQL aggregate functions (STDEV, STDEVP, VAR, VARP, CHECKSUM_AGG) and ModelBuilder extensions. Register with services.AddCommonLinqSqlServerExpression().
6
KitSysCo.BuildingBlocks.CommonLinq.SqlServer
SQL Server support for KitSysCo.BuildingBlocks.CommonLinq: EF Core SQL Server function-call builders (DateDiff, DateFromParts, PatIndex, IsDate, IsNumeric, FreeText, Contains), SQL aggregate functions (STDEV, STDEVP, VAR, VARP, CHECKSUM_AGG) and ModelBuilder extensions. Register with services.AddCommonLinqSqlServerExpression().
5
KitSysCo.BuildingBlocks.CommonLinq.SqlServer
SQL Server support for KitSysCo.BuildingBlocks.CommonLinq: EF Core SQL Server function-call builders (DateDiff, DateFromParts, PatIndex, IsDate, IsNumeric, FreeText, Contains), SQL aggregate functions (STDEV, STDEVP, VAR, VARP, CHECKSUM_AGG) and ModelBuilder extensions. Register with services.AddCommonLinqSqlServerExpression().
4
KitSysCo.BuildingBlocks.CommonLinq.All
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
4
KitSysCo.BuildingBlocks.CommonLinq.GrpcClient
Grpc Server support for KitSysCo.BuildingBlocks.CommonLinq.
4
KitSysCo.BuildingBlocks.CommonLinq.SqlServer
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
4
KitSysCo.BuildingBlocks.CommonLinq.GrpcServer
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
4
KitSysCo.BuildingBlocks.CommonLinq.GrpcClient
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
4
KitSysCo.BuildingBlocks.CommonLinq.GrpcServer
Grpc Server support for KitSysCo.BuildingBlocks.CommonLinq.
3
KitSysCo.BuildingBlocks.CommonLinq.SqlServer
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
3
KitSysCo.BuildingBlocks.CommonLinq.GrpcClient
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
2
KitSysCo.BuildingBlocks.CommonLinq.GrpcServer
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
2
KitSysCo.BuildingBlocks.CommonLinq.SqlServer
Build .NET LINQ expression trees from serializable, language-neutral (protobuf) contracts. Remote clients describe filters, projections, conditionals, arithmetic, method chains and aggregations as data; the server compiles them into real expression trees for IQueryable / EF Core.
2

Version Downloads Last updated
1.0.10 13 10/08/2026
1.0.9 1 10/05/2026
1.0.8 2 10/02/2026
1.0.7 1 10/02/2026
1.0.6 2 09/20/2026
1.0.5 3 09/17/2026
1.0.4 3 09/15/2026
1.0.3 3 09/15/2026
1.0.2 3 09/13/2026
1.0.1 4 09/12/2026