Sylin.Koan.Data.Core
0.21.0
dotnet add package Sylin.Koan.Data.Core --version 0.21.0
NuGet\Install-Package Sylin.Koan.Data.Core -Version 0.21.0
<PackageReference Include="Sylin.Koan.Data.Core" Version="0.21.0" />
<PackageVersion Include="Sylin.Koan.Data.Core" Version="0.21.0" />
<PackageReference Include="Sylin.Koan.Data.Core" />
paket add Sylin.Koan.Data.Core --version 0.21.0
#r "nuget: Sylin.Koan.Data.Core, 0.21.0"
#:package Sylin.Koan.Data.Core@0.21.0
#addin nuget:?package=Sylin.Koan.Data.Core&version=0.21.0
#tool nuget:?package=Sylin.Koan.Data.Core&version=0.21.0
Sylin.Koan.Data.Core
Data access core for Koan: common primitives, options, and helpers used by relational/document/vector providers and apps.
- Target framework: net10.0
- License: Apache-2.0
Capabilities
- Entity contracts and helpers for aggregate storage
- Options and conventions shared across data adapters
- Data-owned adapter option binding for readiness and default paging
- Immutable named-source lifecycle/access policy enforced before readiness or provider work
- Separate host-owned reachability, declared-shape validation, and authorized provisioning stages
- Source-only inspection and registered reads without an Entity repository shim
- Bounded provider-neutral
RecordSetmaterialization and compiled ordinal DTO projection - Compact provider-neutral aggregate mapping with host-scoped compiled plans and shared physical encodings
- Selection-aware health semantics for data connectors
- Support for paging, streaming, and batching semantics (see references)
Install (minimal setup)
dotnet add package Sylin.Koan.Data.Core
Usage - quick examples
- Prefer first-class model statics for top-level data access in your app models:
Item.All(ct)Item.Query(predicate, ct)Item.FirstPage(size, ct)andItem.Page(pageNumber, pageSize, ct)Item.QueryStream(predicate, ct)
AllStream/QueryStreamlazily compose numbered pages only when the selected adapter proves provider-bounded paging and complete ordering. Otherwise they reject correctively before query/yield; there is no materializing fallback.batchSizebounds Koan-visible candidates, not opaque driver buffers. No public cursor or resume-token API exists.- If a first-class static isn’t available, you can fall back to the generic facade (second-class):
Data<TEntity, TKey>.Query(...).
Host-owned persistence policy composes beside the normal zero-configuration bootstrap:
services.AddKoan(() =>
Item.Lifecycle.BeforeUpsert(context =>
context.Current.Price < 0
? context.Cancel("Price cannot be negative.", "item.price")
: context.Proceed()));
The same Lifecycle boundary governs Entity/Data calls and generated REST/MCP entity operations. See
/docs/reference/data/entity-lifecycle.md for phases, bulk/transaction semantics, and deliberate
bypasses.
Use a named source when the application needs to inspect or query an external system without adopting Entity persistence. The connector supplies the final native binding leaf; the application declares the business name, parameters, lane, and bounds once:
services.AddKoan(koan =>
{
koan.Data.Source("LegacyErp")
.Query("orders.recent", query => query
.Lane("Reports")
.Sql("select ... where CREATED_UTC >= @since")
.Parameter<DateTimeOffset>("since")
.MaxRecords(500));
});
var source = Data.Source("LegacyErp");
var description = source.Describe();
var explanation = source.Explain("orders.recent");
var diagnosis = await source.Doctor(ct);
var recent = await source.Query("orders.recent", new { since }, ct);
var orders = recent.Project<RecentOrder>();
var containers = await source.Inspect().Containers(50, ct: ct);
The runtime call cannot change the provider payload, source, or read lane. Opaque bindings require a
provider-enforced read lane. Inspection uses neutral containers rather than schemas/tables, and RecordSet preserves
duplicate fields, missing/null, nested values, explicit bounds, and honest completion. Describe and Explain are
pure and never activate a client; Doctor is explicit, bounded, non-mutating, and returns stable corrective findings.
Map an Entity to an external record shape in the same source declaration:
services.AddKoan(koan =>
{
koan.Data.Source("LegacyErp").Map<Customer>(map => map
.Container("dbo", "CUSTOMER")
.Key(customer => customer.Id).Name("CUSTOMER_NO")
.Property(customer => customer.Name.Full).Name("DISPLAY_NM")
.Property(customer => customer.Profile).Object("PROFILE_JSON"));
});
Name is a scalar physical value, Object keeps one logical subtree together, and Path("NAME_DATA", "full")
places a flat logical value inside a structured physical value. Key(...).Parts(...), Generated, ReadOnly, and
Codec remain explicit only when needed.
When insert/update distinction matters, ask for it explicitly; ordinary Save stays the low-ceremony default:
var result = await item.SaveWithOutcome(ct);
// result.Outcome is Inserted or Updated; result.CommitOutcome is Committed.
This stronger path is available only when the selected adapter proves exact native mutation outcomes. Bulk writes prepare the complete Entity set, dispatch once, and reject an inexact affected-count receipt without replay.
Child relationships are strict by default. Native and in-memory providers execute directly; a
scan-backed provider fails with a corrective RelationshipQueryRejectedException unless the call
chooses a finite budget:
var children = await todo.GetChildren<TodoItem>(
RelationshipQueryPolicy.Bounded(maxCandidates: 1_000, maxResults: 200), ct);
This policy bounds candidates before rows escape and never returns a partial relationship.
Load every declared direct edge with the same Relatives operation for one Entity, a finite
selection, or a provider-bounded stream. Model and key types are inferred:
var graph = await todo.Relatives(ct);
var graphs = await todos.Where(todo => !todo.IsCompleted).Relatives(ct);
await foreach (var current in Todo.QueryStream(todo => !todo.IsCompleted).Relatives(ct))
{
// current.Entity, current.Parents, current.Children
}
Finite and stream forms preserve source order and multiplicity. Parent edges use batched keyed reads;
child edges retain the same strict or explicitly bounded negotiation and runtime facts as
GetChildren<TChild>.
Required Entity/Data operations without a usable Koan host throw KoanHostContextException. Its
Failure, Operation, and RequiredService properties distinguish an absent host, a disposed host,
and a host where the Data module was not composed.
EntityContext is deliberately Data-specific: it scopes source, adapter, partition, cache, and
transaction routing. It stores that state in Core's logical-flow KoanContext, but it is not the
generic API for tenancy, subjects, or other module-owned axes. Those modules own their business-facing
facades and register durable carriage independently through Koan.Core.Context.
Named sources default to Managed + ReadWrite. Integrate an existing system without granting Koan
shape or data mutation authority by declaring the two independent ceilings once:
{
"Koan": {
"Data": {
"Sources": {
"LegacyErp": {
"Adapter": "sqlserver",
"ConnectionString": "<provider-enforced read-only route>",
"StorageLifecycle": "External",
"Access": "ReadOnly"
}
}
}
}
}
Entity, batch, instruction, and Direct paths consume the same immutable source plan. ReadOnly
blocks data writes; External independently blocks create/alter/drop/repair. Forbidden and opaque
effects reject before lifecycle callbacks, readiness, resource creation, or provider I/O. Provider
credentials remain the security boundary.
For a synchronous console process, new ServiceCollection().StartKoan() starts a standard .NET
Generic Host and returns its active provider facade. The caller owns it; use
using var app = (IDisposable)services.StartKoan() so disposal stops hosted capabilities and releases
the ambient Koan host binding. ASP.NET Core and workers continue to use their native host builder with
AddKoan().
Boundaries and failures
- Data Core is the Entity runtime and provider-election owner; it does not provide storage by itself. Reference a connector or use the JSON floor carried by a Koan entry bundle.
- Its dependency on Cache Abstractions is inert contract vocabulary. Referencing Data Core does not activate caching.
- Stream-shaped Entity APIs run only when the elected provider proves bounded paging. Unsupported providers reject before yielding instead of hiding whole-source materialization.
- Query lifecycle callbacks observe only the final visible rows after residual filtering, sort/page completion, and projection; discarded provider candidates never escape through Lifecycle.
- Atomic batches and exact per-item outcomes are negotiated execution seams.
RequireAtomicrejects before deferred mutation loads or callbacks when the selected adapter cannot prove it. - Relationship expansion is direct-edge and budgeted. It is not recursive graph traversal, snapshot isolation, or a promise that scan-backed providers can execute without an explicit candidate limit.
EntityContextowns Data routing dimensions only; tenancy, subject, and other semantic axes are contributed and enforced by their owning modules.- Canonical
PatchPayloadis provider-neutral. Web/MCP projections own protocol parsing and normalization before the operation reaches Data. - Required Entity operations without a live composed host throw
KoanHostContextExceptionwith the missing operation/service correction. - A business operation is never treated as a missing-shape probe, provisioned from exception text, and replayed. Adapters translate native types/codes through the Data failure-classification seam.
- Registered reads are uncached and never replayed. Unknown effects, missing/unenforced lanes, parameter drift, scalar cardinality drift, extra result channels, and active segmentation reject before unsafe exposure.
- Direct typed ADO queries share
RecordSet's compiled ordinal projection; they do not serialize dictionaries to JSON.
Customization
- Configuration and advanced usage are documented in TECHNICAL.md.
References
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Configuration (>= 10.0.10)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.10)
- Microsoft.Extensions.Configuration.EnvironmentVariables (>= 10.0.10)
- Microsoft.Extensions.Configuration.Json (>= 10.0.10)
- Microsoft.Extensions.DependencyInjection (>= 10.0.10)
- Microsoft.Extensions.Hosting (>= 10.0.10)
- Sylin.Koan.Cache.Abstractions (>= 0.20.2 && < 0.21.0)
- Sylin.Koan.Core (>= 0.20.7 && < 0.21.0)
- Sylin.Koan.Data.Abstractions (>= 0.21.0 && < 0.22.0)
NuGet packages (56)
Showing the top 5 NuGet packages that depend on Sylin.Koan.Data.Core:
| Package | Downloads |
|---|---|
|
Sylin.Koan.Web
Controller-first ASP.NET Core projection for Koan Entities with health, startup composition, and redacted runtime facts. |
|
|
Sylin.Koan
Tested Koan foundation bundle: core runtime, Entity data, local Communication, and the JSON connector. |
|
|
Sylin.Koan.Data.Vector
Entity-first Koan vector runtime: provider election, memoized repository resolution, isolation-aware naming, querying, health participation, and facades. |
|
|
Sylin.Koan.Data.Relational
Shared schema governance, SQL translation, and command mechanics for Koan relational Data providers. |
|
|
Sylin.Koan.Data.Connector.Mongo
MongoDB data provider for Koan: options binding and repository integration for document databases. |
GitHub repositories
This package is not used by any popular GitHub repositories.