RelationQuery.Expression()
- What it contributes
- A typed authoring session that lowers expressions into canonical relation/query IR.
Cohesive.Relations Guide
Author, evaluate, and materialize a typed relation through the canonical expression API. Start with one supplied CLR object, then add related facts only when the definition needs them.
The expression surface is the normal application API for Cohesive.Relations. Each operation immediately lowers through the canonical authoring core, so the typed C# definition and the portable relation/query document do not become competing models.
This guide creates a rooted Load → LoadDto relation, executes it from a supplied object, and compiles the canonical result into a CLR DTO. It then extends the definition with a Customer relationship.
Add the current prerelease package to a .NET 10 project:
dotnet add package Cohesive.Relations --prereleaseImport the namespaces used by this walkthrough:
using System.Text.Json.Serialization;
using Cohesive.Relations.Authoring;
using Cohesive.Relations.Execution;
using Cohesive.Relations.Mapping;The package is under active development. Keep the application and documentation on the same prerelease while APIs are evolving.
Start with ordinary CLR types. Serialized names participate in the deterministic shape metadata discovered by the authoring session.
public sealed class Load
{
[JsonPropertyName("id")]
public required string Id { get; init; }
[JsonPropertyName("customerId")]
public required string CustomerId { get; init; }
[JsonPropertyName("status")]
public required string Status { get; init; }
}
public sealed class LoadDto
{
[JsonPropertyName("id")]
public required string Id { get; init; }
[JsonPropertyName("status")]
public required string Status { get; init; }
}Source<T>() discovers a graph-qualified shape document from the CLR contract. Explicit imported shape documents and member-path overrides remain available for tooling and schemas that should not be convention-derived.
Create an expression authoring session, declare the logical source, project the DTO, and finish with a rooted relation terminal:
var author = RelationQuery.Expression();
var loads = author.Source<Load>();
var loadDtos = author.Project(
loads,
(Load load) => new LoadDto
{
Id = load.Id,
Status = load.Status
});
var relation = loadDtos.BuildRelation(dto => dto.Id);That is the complete semantic definition. The authoring calls have already produced canonical source, projection, assignment, binding, shape, identity, and relation-output structures.
Inspect relation.Validation for structured authoring diagnostics. When persistence or transport is needed, relation.CreateDocument() creates the validated and fingerprinted canonical envelope.
A definition is reusable. An evaluation is one invocation with an explicit identity, parameter values, selected outputs, and supplied-root evidence.
Create one Load and supply it to an evaluation:
var load = new Load
{
Id = "load-42",
CustomerId = "customer-7",
Status = "Open"
};
var evaluation = author
.Evaluate(relation, new("load-dto/load-42"))
.Supply([load], static value => value.Id)
.Build();The evaluation remains target-neutral. It does not contain a database choice, source placement, adapter binding, or driver configuration.
For this one-source mapping, execute the complete canonical pipeline without configuring any I/O:
var outcome = await RelationQueryEvaluator
.CreateSuppliedOnly()
.EvaluateAsync(evaluation);
if (!outcome.IsSuccessful)
{
throw new InvalidOperationException(
$"Evaluation ended with {outcome.Status}; inspect its diagnostics.");
}CreateSuppliedOnly() is deliberately narrow: it accepts a compiled relation plan with one supplied-root source and no retained relationship traversal. It still performs canonical compilation, realization, physical planning, interpretation, and result construction.
Canonical interpretation produces relation output rows. Compile a CLR construction kernel against the exact plan, then map those rows into LoadDto values:
var mapperCompilation =
RelationDtoMapperCompiler.Default.Compile<LoadDto>(
outcome.Compilation.Plan!);
if (!mapperCompilation.IsSuccessful)
{
throw new InvalidOperationException(
string.Join(Environment.NewLine, mapperCompilation.Diagnostics));
}
var mapping = mapperCompilation.Mapper!.Map(
outcome.PhysicalExecution!);
var dto = mapping.Rows.Single().Value;
Console.WriteLine($"{dto.Id}: {dto.Status}");
// load-42: OpenThe mapper performs CLR construction and conversion over canonical output. It does not execute filters, acquire related observations, or maintain a second mapping definition.
Now introduce a related Customer and a flattened output:
public sealed class Customer
{
[JsonPropertyName("id")]
public required string Id { get; init; }
[JsonPropertyName("name")]
public required string Name { get; init; }
}
public sealed class LoadSearchDto
{
[JsonPropertyName("id")]
public required string Id { get; init; }
[JsonPropertyName("customerId")]
public required string CustomerId { get; init; }
[JsonPropertyName("customerName")]
public string? CustomerName { get; init; }
}Declare Load.CustomerId → Customer identity inline with Traverse:
var customers = author.Traverse<Load, Customer>(
loads,
load => load.CustomerId);
var searchDocuments = author.Project(
customers,
(Load load, Customer customer) => new LoadSearchDto
{
Id = load.Id,
CustomerId = load.CustomerId,
CustomerName = customer.Name
});
var enrichedRelation =
searchDocuments.BuildRelation(dto => dto.Id);The authoring session retains the convention-derived relationship in author.CreateRelationshipCatalogDocument(). The persisted definition records the source reference and target identity semantics; the traversal retains its direction, cardinality, and input requirement.
At runtime, the same traversal may become a co-located PostgreSQL join, bounded Customer acquisition followed by local correlation, or in-memory interpretation over already supplied evidence. The relation does not change when that physical choice changes.
Unlike the one-source example, evaluating the enriched relation requires an application evaluator configured with source placement, bounded planning policy, and source readers—or explicit runtime evidence for both facts.
After evaluation, the outcome retains the phase artifacts instead of flattening them into an opaque DTO response:
This retained evidence is what allows the same relation to produce a DTO, dependency manifest, lineage report, target artifact, or explain document without making any of those derived artifacts the semantic source of truth.