Table of Contents

Errors

Every Rinku exception has a code. The code is also available through RinkuException.Code and the matching documentation entry is exposed through HelpLink.

public record Album(int Id, string Title);
static readonly QueryCommand GetAlbum = new("SELECT AlbumId AS Id, Title FROM albums WHERE AlbumId = @albumId");

try
{
    Album album = GetAlbum.Query<Album>(cnn, new { albumId = 12 });
}
catch (RinkuException error)
{
    logger.LogError("{Code} {Message} {Help}", error.Code, error.Message, error.HelpLink);
}

A family can be caught directly and one code can be matched.

catch (RinkuReadException error) when (error.Code == ErrorCodes.NoRows)
{
    return null;
}
Family Codes
RinkuTemplateException RINKU1###
RinkuBindingException RINKU2###
RinkuMappingException RINKU3###
RinkuReadException RINKU4###
RinkuConfigurationException RINKU5###
RinkuTrackingException RINKU6###
RinkuInternalException RINKU9###

Each family derives from RinkuException.

Template errors

RINKU1001 query too short

QueryCommand command = new("x");
// RINKU1001

The query template needs at least two characters.

RINKU1002 unclosed comment

SELECT /*IncludeTitle Title FROM albums
SELECT /*IncludeTitle*/Title FROM albums
SELECT /*~ application note */Title FROM albums

Template syntax

RINKU1003 empty condition key

SELECT /**/Title FROM albums
SELECT /*Visible&*/Title FROM albums
SELECT /*Visible*/Title FROM albums
SELECT /*Visible&Published*/Title FROM albums

Each condition operand needs a key.

Markers

RINKU1004 unknown handler suffix

SELECT AlbumId AS Id FROM albums WHERE AlbumId IN (@albumIds_Q)
SELECT AlbumId AS Id FROM albums WHERE AlbumId IN (@albumIds_X)

_X is the built in collection expansion suffix. Application handlers can register other suffixes.

Value handlers · Custom handlers

RINKU1005 condition variable not in the query

SELECT AlbumId AS Id FROM albums WHERE /*@missing*/IsArchived = 0

A variable marker must name a variable in the template.

SELECT AlbumId AS Id FROM albums WHERE /*@albumId*/AlbumId = @albumId

A custom condition key is independent from query variables.

SELECT AlbumId AS Id FROM albums WHERE /*Current*/IsArchived = 0

Markers

RINKU1006 unbalanced scope

SELECT AlbumId) FROM albums
SELECT CASE WHEN IsArchived = 1 THEN 0 ELSE 1 END END FROM albums

A closing parenthesis or END has no matching open scope.

Template syntax

RINKU1007 scope too deep

64 nested parentheses, CASE blocks, or BEGIN blocks
RINKU1007

The template parser supports 63 nested scopes.

RINKU1008 projection only construct

SELECT AlbumId AS Id!, Title FROM albums

The same marker is valid in a dynamic projection.

?SELECT AlbumId AS Id!, Title FROM albums

Dynamic projection

Binding errors

RINKU2001 no connection

command.Connection == null
parser.Query(command)
RINKU2001

A command executed through Rinku needs a connection.

using DbCommand command = cnn.CreateCommand();
command.CommandText = "SELECT AlbumId AS Id FROM albums";
Album album = parser.Query(command);

Existing DbCommand

RINKU2002 required handler value

static readonly QueryCommand ById = new("SELECT AlbumId AS Id, Title FROM albums WHERE AlbumId IN (@albumIds_X)");
List<Album> albums = ById.Query<List<Album>>(cnn);
// RINKU2002

A conditional handler can disappear with an absent value.

static readonly QueryCommand ById = new("SELECT AlbumId AS Id, Title FROM albums WHERE AlbumId IN (?@albumIds_X)");
List<Album> albums = ById.Query<List<Album>>(cnn);
// SELECT AlbumId AS Id, Title FROM albums

Collection expansion

RINKU2003 handler value type

values.Use("@skip", "not a number");
// RINKU2003 when the template uses @skip_N.
values.Use("@skip", 46);
values.Use("@skip", "46");

The handler decides which value types it accepts.

Value handlers

RINKU2004 invalid parameter at index

command.Parameters[index]
object is not IDbDataParameter
RINKU2004

This can occur with an application supplied IDbCommand implementation whose parameter collection returns an invalid item.

Existing DbCommand

RINKU2005 value not set

MultiVariableHandler handler = MultiVariableHandler.Build("@albumIds");
object? state = null;
handler.Update(command, ref state, new[] { 2, 5 });
// RINKU2005

Update expects state created by SaveUse. Builders perform that sequence for query execution.

Custom conditional SQL

RINKU2006 type carries no size

SizedDbParamCache.Get(DbType.Int32, 100);
// RINKU2006

Size metadata applies to database types that carry a size.

SizedDbParamCache.Get(DbType.String, 100);

Parameter metadata

Mapping errors

RINKU3001 no parser for the schema

The returned columns must satisfy one construction path for the requested type.

SELECT AlbumId, AlbumTitle FROM albums
public record Album(int Id, string Title);
// AlbumId and AlbumTitle do not match Id and Title.

The SQL can adapt the names.

SELECT AlbumId AS Id, AlbumTitle AS Title FROM albums

The type can adapt the names instead.

public record Album([Alt("AlbumId")] int Id, [Alt("AlbumTitle")] string Title);

A nested type also needs a mapping registration when it is reached through another type.

public record Artist(int Id, string Name) : IDbReadable;
public record Album(int Id, string Title, Artist Artist);

Objects · Names · Registration

RINKU3002 missing group boundary

public record Report(List<int> Rows, int Total);
static readonly QueryCommand GetReport = new("SELECT RowId AS Rows, COUNT(*) OVER () AS Total FROM report_rows ORDER BY RowId");
Report report = GetReport.Query<Report>(cnn);
// RINKU3002 when no usable parent boundary can be negotiated.

A multi-row mapping needs a boundary for one complete parent value.

Grouping

RINKU3003 group key matched no column

[GroupKeyColumns("AccountId")]
public record AccountSummary(string Name, List<int> InvoiceIds);
SELECT Name, InvoiceId AS InvoiceIds FROM invoices
-- AccountId is missing, RINKU3003.

The required grouping key must match the returned schema.

Grouping

RINKU3004 conflicting grouping rules

public sealed class Batch
{
    [GroupKeyMethod(nameof(ByWindow))]
    public Batch([GroupKey] int id, List<string> items) { }

    public static (bool Same, int Next) ByWindow(int stored, int current) => default;
}

The same construction declares two grouping rule families.

Grouping

Reading errors

RINKU4001 no results

static readonly QueryCommand FindAlbum = new("SELECT AlbumId AS Id, Title FROM albums WHERE AlbumId = @albumId");

Album album = FindAlbum.Query<Album>(cnn, new { albumId = 999 });
// RINKU4001 when no complete Album is returned.

A result shape can represent absence.

Optional<Album> album = FindAlbum.Query<Optional<Album>>(cnn, new { albumId = 999 });
List<Album> albums = FindAlbum.Query<List<Album>>(cnn, new { albumId = 999 });

Result shapes

RINKU4002 result shape refused the results

Album album = GetAlbum.Query<Single<Album>>(cnn, new { albumId = 12 });
// RINKU4002 when a second complete Album exists.

Single result shapes

RINKU4003 database NULL not allowed

public record Album(int Id, int ReleaseYear);
// NULL ReleaseYear produces RINKU4003.
public record Album(int Id, int? ReleaseYear);

Database NULL

RINKU4004 cannot convert

static readonly QueryCommand GetTitle = new("SELECT Title FROM albums WHERE AlbumId = @albumId");
int title = GetTitle.ExecuteScalar<int>(cnn, new { albumId = 12 });
// RINKU4004 when Title cannot be converted to int.
string title = GetTitle.ExecuteScalar<string>(cnn, new { albumId = 12 });

Construction and conversion

RINKU4005 cannot read a dynamic column

Version invalidId = row.Get<Version>("Id");
// RINKU4005 for an integer Id column.

int id = row.Get<int>("Id");

Dynamic rows

Configuration errors

RINKU5001 type not usable by this parsing info

A custom TypeParsingInfo can reject a target type it does not support.

Type registration

RINKU5002 construction shape not usable

static class BoxFactory
{
    public static Box<T> Create<T>(T value) => new(value);
}

An offered constructor or factory must have a construction shape that the target mapping can use.

Construction paths

RINKU5003 unusable member

public class Row
{
    public int Id { get; }
}

PropertyInfo id = typeof(Row).GetProperty(nameof(Row.Id)) ?? throw new InvalidOperationException();
TypeParsingInfo.GetOrAdd<Row>().AddMember(id);
// RINKU5003 because Id has no setter.

Mapping slot rules

RINKU5004 target type mismatch

TypeParsingInfo info = TypeParsingInfo.GetOrAdd<Album>();
ConstructorInfo paymentConstructor = typeof(Payment).GetConstructors()[0];
info.AddPossibleConstruction(paymentConstructor);
// RINKU5004

The registered construction builds a different target type.

Construction paths

RINKU5005 construction from a foreign generic type

An open generic construction cannot be taken from an unrelated generic host.

A non generic host can expose a generic factory.

static class BoxFactory
{
    public static Box<T> Create<T>(T value) => new(value);
}

Construction paths

RINKU5006 attribute on the wrong member type

public class Options
{
    [ForBoolCond]
    public int IncludeDeleted { get; init; }
}
// RINKU5006
public class Options
{
    [ForBoolCond]
    public bool IncludeDeleted { get; init; }
}

Parameter members

RINKU5007 operation unsupported for this type

JsonSerializer.Deserialize<DynaObject>("{}", options);
// RINKU5007

DynaObject gets its shape from a result schema and can be serialized after it has that shape.

Dynamic rows

Tracking errors

RINKU6001 no copy strategy

The runtime tracking materializer could not build the requested tracked shape.

Runtime tracking

RINKU6002 copy method not usable

A configured tracking source method or property does not match the tracked member value type.

Tracking items · Runtime tracking

RINKU6003 no current value

tracked slot has no current value
slot is read for display
RINKU6003

Tracking items

RINKU6004 no factory for a new item

IBindingList binding = tracked;
binding.AddNew();
// RINKU6004 when no new item factory or AddingNew handler provides an item.

Tracking binding

Internal errors

RINKU9001 internal invariant

RINKU9001 reports an internal Rinku invariant failure.

RINKU9001
include the stack trace, query template, target type, and result schema when available

GitHub issues