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
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.
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
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.
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
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);
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
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.
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.
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.
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);
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.
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.
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.
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 });
RINKU4002 result shape refused the results
Album album = GetAlbum.Query<Single<Album>>(cnn, new { albumId = 12 });
// RINKU4002 when a second complete Album exists.
RINKU4003 database NULL not allowed
public record Album(int Id, int ReleaseYear);
// NULL ReleaseYear produces RINKU4003.
public record Album(int Id, int? ReleaseYear);
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 });
RINKU4005 cannot read a dynamic column
Version invalidId = row.Get<Version>("Id");
// RINKU4005 for an integer Id column.
int id = row.Get<int>("Id");
Configuration errors
RINKU5001 type not usable by this parsing info
A custom TypeParsingInfo can reject a target type it does not support.
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.
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.
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.
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);
}
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; }
}
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.
Tracking errors
RINKU6001 no copy strategy
The runtime tracking materializer could not build the requested tracked shape.
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
RINKU6004 no factory for a new item
IBindingList binding = tracked;
binding.AddNew();
// RINKU6004 when no new item factory or AddingNew handler provides an item.
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