KitSysCo.BuildingBlocks.CommonLinq.GrpcServer 1.0.8
BuildingBlocks.CommonLinq
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.jsfor 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/VARaggregates): EF Core + SQL Server, via theKitSysCo.BuildingBlocks.CommonLinq.SqlServeradd-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).ConfigureAwait(false);
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,NegateMath.—Abs,Acos,Asin,Atan,Ceiling,Cos,Cosh,Exp,Floor,Log,Max,Min,Pow,Round,Sign,Sin,Sinh,Sqrt,Tan,Tanh,Truncatestring./str.—Concat,Join,IsNullOrEmpty,IsNullOrWhiteSpace,Trim,TrimStart,TrimEndDateTime.—DaysInMonth,IsLeapYearEF.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 (aCommonLinqExpression)sort— fields/expressions, ascending/descendingproject— init-object projectiongroup— keys + aggregates (SUM,AVG, …) + optionalhavingjoin—Inner/Left/Right/Full, from another source/datasetpage—skip/takedistinct,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 ownexpr_custom_ruleopsICommonLinqExpressionOperationRulesBuilder,ICommonLinqFunctionCallExpressionBuilderService,ICommonLinqMethodCallExpressionBuilderService, … — add operators, functions, or method-call kindsICommonLinqTypeResolver— control howclrTypenames resolve toTypes (default: assembly-qualified/full/simple name across loaded assemblies)ICommonLinqRuntimeTypeGenerator— control howisRuntimeprojections materialize a type (default: emits and caches a runtime class viaReflection.Emit, falling back toDictionary<string, object?>)ICommonLinqQueryDatasetTransformHandler— implement dataset-transform execution (filter/sort/group/join/…) for your own query pipeline- Satellite packages — the
BuildingBlocks.CommonLinq.SqlServerproject 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 tomain: restore → build (Release) → pack, and uploads the.nupkgas a build artifact. Restore usesnuget.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.0.8
git push origin v1.0.8
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 tohttps://api.nuget.org/v3/index.json. Set it tohttps://kitsys-nuget.tvps.kitsysco.com/v3/index.jsonto 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.GrpcServer.
| Packages | Downloads |
|---|---|
|
KitSysCo.BuildingBlocks.CommonLinq.DependencyInjection
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.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.DependencyInjection
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.DependencyInjection
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.
|
1 |
.NET 10.0
- KitSysCo.BuildingBlocks.CommonLinq (>= 1.0.8)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.12)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.12)
- Ardalis.GuardClauses (>= 5.0.0)
- Mapster (>= 10.0.11)
- Grpc.Core (>= 2.46.6)
- Google.Protobuf (>= 3.36.1)
- Serilog (>= 4.4.0)
.NET 8.0
- Serilog (>= 4.4.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.12)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.12)
- Ardalis.GuardClauses (>= 5.0.0)
- Mapster (>= 10.0.11)
- Grpc.Core (>= 2.46.6)
- KitSysCo.BuildingBlocks.CommonLinq (>= 1.0.8)
- Google.Protobuf (>= 3.36.1)