Skip to main content

C# and .NET Customization

Treat the generated project as a replaceable domain API. Put application services, dependency injection, authenticated context construction, policies, and external input adapters in handwritten projects.

Register infrastructure once

Use the host's dependency injection container for long-lived provider resources and a scoped factory for UserContext:

public sealed record TrustedRequest(
long TenantId,
string ActorId,
IReadOnlySet<string> Permissions);

public sealed class TeaqlContextFactory
{
public Task<UserContext> CreateAsync(TrustedRequest trusted)
{
var ctx = new UserContext();
// Register generated metadata, provider, policy and two audit sinks.
// Add trusted identity/authorization; never copy them from body JSON.
return Task.FromResult(ctx);
}
}

Use released registration APIs after publication. The architectural invariant is one context argument at every execution/save terminal.

Map dynamic input explicitly

Deserialize into a narrow transport DTO, validate limits and types, then switch over allowlisted fields/operators/sorts. Do not use reflection to turn user strings into generated method calls and never interpolate application SQL to fill an API gap.

request = filter switch
{
{ Field: "order_number", Operator: "contains" } =>
request.WithOrderNumberContaining(RequireString(filter.Value)),
_ => throw new InvalidQueryException("Unsupported field or operator")
};

Tenant, merchant, actor, permissions, provider, request policy and purpose policy are always server-owned.

Domain behavior versus platform policy

Use generated behavior/checker contracts for entity-specific validation, normalization and lifecycle rules. Use context-level request policy for tenant scope, authorization, approved purposes, paging caps, and raw-query restrictions. Register both at application startup and test through generated Requests.

Audit sinks

AuditAs declares mutation reason. Preserve the runtime row/mutation audit path, then optionally register an application sink for an outbox, compliance stream, or telemetry. The application event must retain trusted actor attribution and masking. It does not replace immutable runtime audit.

Provider and transaction boundary

Configure connection factories/pools through DI and context assembly. Keep provider selection out of controllers and client JSON. Test transaction rollback, optimistic locking, reconnect, native aggregates, stable pagination and relation Top-N for every provider claimed in deployment documentation.

Packaging after release

Pin official NuGet packages centrally, commit the lock file where applicable, and test restore/build in a clean environment. Until official coordinates exist, leave the installation command blank rather than publishing an unverifiable package name.

If customization requires an edit in generated Q.cs, E.cs, Requests, or Models, move it to KSML, the generator, or a runtime extension point and regenerate.