Table of Contents

Parameter source rules

Built in presence rules

public sealed class AlbumSearch
{
    [NotNullOrWhitespace]
    public string? Title { get; init; }

    [NotDefault]
    public int MinimumYear { get; init; }
}

Supplying values

Custom member condition

static class SearchRules
{
    public static bool HasText(string? value) => !string.IsNullOrWhiteSpace(value);
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
sealed class HasTextAttribute : AccessorEmitterHandler
{
    static readonly MethodConditionEmitter Emitter = new(
        typeof(SearchRules).GetMethod(nameof(SearchRules.HasText))
        ?? throw new InvalidOperationException("HasText was not found."));

    public override IAccessorEmitter? GetMemberEmitter(char variableCharacter, int index, Type type, MemberInfo member, Mapper mapper)
        => index < 0 ? null : Emitter;
}
public sealed class AlbumSearch
{
    [HasText]
    public string? Title { get; init; }
}

static readonly QueryCommand SearchAlbums = new("SELECT AlbumId AS Id, Title FROM albums WHERE Title = ?@title");

List<Album> albums = SearchAlbums.Query<List<Album>>(cnn, new AlbumSearch { Title = "   " });
// SELECT AlbumId AS Id, Title FROM albums

albums = SearchAlbums.Query<List<Album>>(cnn, new AlbumSearch { Title = "Blue" });
// SELECT AlbumId AS Id, Title FROM albums WHERE Title = @title

Type rule for a key without a member

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
sealed class IncludeDeletedAttribute : AccessorEmitterHandler
{
    static readonly IncludeDeletedEmitter Emitter = new();

    public override ITypeAccessorEmitter? GetTypeEmitter(char variableCharacter, int index, Type type, Mapper mapper)
        => mapper.GetIndex("IncludeDeleted") == index ? Emitter : null;
}

sealed class IncludeDeletedEmitter : TypeAccessorEmitterBase
{
    protected override void EmitCondition(ILGenerator il, Type type) => il.Emit(OpCodes.Ldc_I4_1);

    protected override void EmitValue(ILGenerator il, Type type)
    {
        il.Emit(OpCodes.Ldc_I4_1);
        il.Emit(OpCodes.Box, typeof(bool));
    }
}

[IncludeDeleted]
public sealed record AlbumSearch(int ArtistId);

The parameter type has no IncludeDeleted member. The type rule supplies that query key.

ITypeAccessorEmitter handles a key supplied by the type. IAccessorEmitter handles an existing member slot.