-
Notifications
You must be signed in to change notification settings - Fork 0
API Reference
Every public type and member, by package. Behaviour is described on the linked pages; this is the signature list.
All packages target .NET 10 and are version 2.0.0.
No dependencies. Namespace PepperX.QueryForge unless stated.
public class Query
{
public QueryCriteria Criteria { get; set; }
public QueryPaging Paging { get; set; }
public IReadOnlyList<string> SelectColumns { get; set; }
public IReadOnlyList<SortDescriptor> SortColumns { get; set; }
public IReadOnlyList<GroupByDescriptor> GroupByColumns { get; set; }
}
public record QueryCriteria
{
public IReadOnlyList<ConditionGroup> Groups { get; init; }
public Logic Logic { get; init; }
public QueryCriteria(IReadOnlyList<ConditionGroup>? groups = null, Logic logic = Logic.And);
}
public record ConditionGroup
{
public IReadOnlyList<Condition> Conditions { get; init; }
public Logic Logic { get; init; }
public ConditionGroup(IReadOnlyList<Condition>? conditions = null, Logic logic = Logic.And);
}
public record Condition(
string ColumnName,
ConditionOperator Operator,
object? Value = null,
object? ValueTo = null);
public record QueryPaging(int Size = 12, int Number = 1);
public record SortDescriptor(string ColumnName, SortOrder SortOrder = SortOrder.Ascending)
: IColumnDescriptor;
public record GroupByDescriptor(string ColumnName, SortOrder SortOrder = SortOrder.Ascending)
: IColumnDescriptor;
public interface IColumnDescriptor
{
string ColumnName { get; }
}See Query Model.
public enum Logic { And, Or, AndNot, OrNot } // 0, 1, 2, 3
public enum SortOrder { Ascending, Descending } // 0, 1
public enum QueryResultType { Flat, Grouped } // 0, 1
public enum QueryValidationMode { SilentStrip, ThrowException } // 0, 1
public enum ConditionOperator
{
Equals, NotEquals, Contains, NotContains, // 0, 1, 2, 3
StartsWith, EndsWith, LessThan, GreaterThan, // 4, 5, 6, 7
LessThanOrEqualTo, GreaterThanOrEqualTo, Between // 8, 9, 10
}public record QueryResult<TModel>
{
public QueryResultMeta Meta { get; init; }
public IReadOnlyList<TModel> Models { get; init; }
public IReadOnlyList<HierarchyNode<TModel>> Groups { get; init; }
}
public record QueryResultMeta(QueryResultMetaTotal Total, QueryResultType Type);
public record QueryResultMetaTotal(int Rows, int Pages);
public record HierarchyNode<TModel>(
object? Key,
int Count,
IReadOnlyList<HierarchyNode<TModel>>? SubGroups,
IReadOnlyList<TModel>? Items);See Results and Metadata.
public static class QueryBuilder
{
public static QueryFluent New();
public static QueryFluent Select(params string[] columns);
public static QueryFluent Where(QueryCriteria criteria);
public static QueryFluent Sort(params SortDescriptor[] sorts);
public static QueryFluent GroupBy(params GroupByDescriptor[] groups);
public static QueryFluent Page(int size, int number = 1);
}
public class QueryFluent
{
public QueryFluent();
public QueryFluent(Query query);
public QueryFluent Select(params string[] columns);
public QueryFluent Where(QueryCriteria criteria);
public QueryFluent Sort(params SortDescriptor[] sorts);
public QueryFluent GroupBy(params GroupByDescriptor[] groups);
public QueryFluent Page(int size, int number = 1);
public Query Build();
}See Fluent Builders.
public static class QueryValidationExtensions
{
public static Query Validate(this Query query,
Action<QueryValidationRules> configure,
QueryValidationMode mode = QueryValidationMode.SilentStrip);
public static Query Validate(this Query query,
QueryValidationRules rules,
QueryValidationMode mode = QueryValidationMode.SilentStrip);
// Extension points used by provider-specific validators.
public static void ApplyBaseRules(Query query, QueryValidationRules rules,
List<string> errors, QueryValidationMode mode);
public static void ThrowIfErrors(List<string> errors, QueryValidationMode mode);
}
public class QueryValidationRules
{
public QueryValidationRules PageSize(Action<QueryPagingRuleBuilder> configure);
public QueryValidationRules Select(Action<QueryColumnRuleBuilder> configure);
public QueryValidationRules Sort(Action<QueryColumnRuleBuilder> configure);
public QueryValidationRules GroupBy(Action<QueryColumnRuleBuilder> configure);
public QueryValidationRules Where(Action<QueryColumnRuleBuilder> configure);
}
public class QueryColumnRuleBuilder
{
public QueryColumnRuleBuilder Allow(params string[] columns);
public QueryColumnRuleBuilder Deny(params string[] columns);
}
public class QueryPagingRuleBuilder
{
public QueryPagingRuleBuilder Max(int max);
public QueryPagingRuleBuilder Min(int min);
}
public class QueryValidationException : Exception
{
public IReadOnlyList<string> InvalidProperties { get; }
public QueryValidationException(string message, IReadOnlyList<string> invalidProperties);
}
public class QueryColumnRule { } // rule state; configured through the builder
public class QueryPagingRule { } // rule state; configured through the builderSee Validation.
Namespace PepperX.QueryForge.Querying. These are the rules every provider routes through — public
so a custom provider can reuse them.
public static class ConditionSemantics
{
public static bool IsDisjunction(Logic logic); // Or / OrNot → true
public static bool IsNegated(Logic logic); // AndNot / OrNot → true
public static bool IsExecutable(Condition condition); // does this contribute a predicate
public static bool IsPatternOperator(ConditionOperator op);
public static object? Unwrap(object? value); // JsonElement / DBNull → CLR value
}
public static class ValueCoercion
{
public static bool TryCoerce(object? value, Type targetType, out object? result);
}
public sealed class QueryValueComparer : IComparer<object?>
{
public static readonly QueryValueComparer Instance;
public int Compare(object? x, object? y);
public bool AreEqual(object? x, object? y);
}
public static class HierarchyBuilder
{
public static IReadOnlyList<HierarchyNode<TModel>> Build<TModel>(
IReadOnlyList<TModel> rows,
IReadOnlyList<GroupByDescriptor> groups,
Func<TModel, string, object?>? valueAccessor = null);
}
public static class ProjectionShaper
{
public static TModel Shape<TModel>(TModel source,
IReadOnlyList<string> selectColumns, IReadOnlyList<string>? alsoKeep = null);
public static IReadOnlyList<TModel> ShapeAll<TModel>(IReadOnlyList<TModel> rows,
IReadOnlyList<string> selectColumns, IReadOnlyList<string>? alsoKeep = null);
public static IReadOnlyList<HierarchyNode<TModel>> ShapeHierarchy<TModel>(
IReadOnlyList<HierarchyNode<TModel>> nodes,
IReadOnlyList<string> selectColumns, IReadOnlyList<string>? alsoKeep = null);
}
public static class PropertyAccessor
{
public static Func<TModel, string, object?> For<TModel>();
public static bool Exists<TModel>(string? columnName);
}
public static class InMemoryQueryEngine
{
public static QueryResult<TModel> Apply<TModel>(
IEnumerable<TModel> source, Query query,
Func<TModel, string, object?>? valueAccessor = null);
public static bool Matches<TModel>(
TModel row, QueryCriteria criteria,
Func<TModel, string, object?> accessor,
Func<string, bool>? columnExists = null);
}See Query Semantics.
Depends on Dapper 2.1.79 and Microsoft.Extensions.DependencyInjection.Abstractions 10.0.9. No database driver.
Namespace PepperX.QueryForge.Dapper.
public class DapperQuery : PepperX.QueryForge.Query
{
public DapperQueryObject? Object { get; set; }
}
public record DapperQueryObject(
string Name,
string Schema = "",
DapperObjectType Type = DapperObjectType.Auto,
IReadOnlyDictionary<string, object?>? Parameters = null);
public enum DapperObjectType { Auto = 0, Table = 1, View = 2, TVF = 3, SP = 4 }
public enum DapperDatabaseProvider { MSSQL, MySQL, PostgreSQL, Oracle, SQLite }public interface IDapperQueryService
{
Task<QueryResult<TModel>> QueryAsync<TModel>(
IDbConnection connection, DapperQuery query,
int? commandTimeout = null, IDbTransaction? transaction = null);
Task<QueryResult<TModel>> QueryAsync<TModel>(
DapperQuery query,
int? commandTimeout = null, IDbTransaction? transaction = null);
}
public static class DapperQueryForgeConnectionExtensions
{
public static Task<QueryResult<TModel>> QueryForgeAsync<TModel>(
this IDbConnection connection, DapperQuery query,
int? commandTimeout = null, IDbTransaction? transaction = null);
}
public static class DapperServiceCollectionExtensions
{
public static IServiceCollection AddQueryForgeDapper(
this IServiceCollection services, Action<DapperQueryForgeOptions>? configure = null);
public static IServiceCollection AddQueryForgeDialect(
this IServiceCollection services, ISqlDialect dialect);
}
public class DapperQueryForgeOptions
{
public Func<IServiceProvider, IDbConnection>? ConnectionFactory { get; set; }
}See Dapper Provider.
public static class DapperQueryBuilder
{
public static DapperQueryFluent New();
public static DapperQueryFluent New(DapperQuery dapperQuery);
public static DapperQueryFluent FromBase(Query baseQuery);
public static DapperQueryFluent ForObject(string name, string schema = "",
DapperObjectType type = DapperObjectType.Auto,
IReadOnlyDictionary<string, object?>? parameters = null);
public static DapperQueryFluent Select(params string[] columns);
public static DapperQueryFluent Where(QueryCriteria criteria);
public static DapperQueryFluent Sort(params SortDescriptor[] sorts);
public static DapperQueryFluent GroupBy(params GroupByDescriptor[] groups);
public static DapperQueryFluent Page(int size, int number = 1);
}
public class DapperQueryFluent : QueryFluent
{
public DapperQueryFluent(DapperQuery query);
public static DapperQueryFluent FromBase(Query baseQuery);
public DapperQueryFluent ForObject(string name, string schema = "",
DapperObjectType type = DapperObjectType.Auto,
IReadOnlyDictionary<string, object?>? parameters = null);
public new DapperQueryFluent Select(params string[] columns);
public new DapperQueryFluent Where(QueryCriteria criteria);
public new DapperQueryFluent Sort(params SortDescriptor[] sorts);
public new DapperQueryFluent GroupBy(params GroupByDescriptor[] groups);
public new DapperQueryFluent Page(int size, int number = 1);
public new DapperQuery Build();
}Note DapperQueryBuilder is in the global namespace, so it needs no using beyond
PepperX.QueryForge.Dapper for its parameter types.
public static class DapperQueryValidationExtensions
{
public static DapperQuery Validate(this DapperQuery query,
Action<DapperQueryValidationRules> configure,
QueryValidationMode mode = QueryValidationMode.SilentStrip);
}
public class DapperQueryValidationRules : QueryValidationRules
{
public DapperQueryValidationRules Object(Action<DapperObjectRuleBuilder> configure);
}
public class DapperObjectRuleBuilder
{
public DapperObjectRuleBuilder AllowSchema(params string[] schemas);
public DapperObjectRuleBuilder DenySchema(params string[] schemas);
public DapperObjectRuleBuilder AllowTable(params string[] tables);
public DapperObjectRuleBuilder DenyTable(params string[] tables);
public DapperObjectRuleBuilder RequireName(bool require = true);
}
public class DapperObjectRule { } // rule state; configured through the builderSee Validation.
Namespace PepperX.QueryForge.Dapper.Compiler.
public sealed class SqlQueryCompiler
{
public SqlQueryCompiler(ISqlDialect dialect);
public ISqlDialect Dialect { get; }
public CompiledSql CompileSchemaProbe(DapperQuery query);
public CompiledSql CompileRows(DapperQuery query, ColumnWhitelist columns);
public CompiledSql CompileRowCount(DapperQuery query, ColumnWhitelist columns);
public CompiledSql CompileGroupKeys(DapperQuery query, ColumnWhitelist columns);
public CompiledSql CompileGroupCount(DapperQuery query, ColumnWhitelist columns);
public CompiledSql CompileGroupRows(DapperQuery query, ColumnWhitelist columns,
IReadOnlyList<object?> groupKeys);
}
public sealed record CompiledSql(string Text, IReadOnlyDictionary<string, object?> Parameters);
public sealed class ColumnWhitelist
{
public ColumnWhitelist(IEnumerable<string> columns);
public ColumnWhitelist(IEnumerable<KeyValuePair<string, Type?>> columns);
public IReadOnlyCollection<string> Columns { get; }
public bool Contains(string? columnName);
public Type? TypeOf(string? columnName);
}
public interface ISqlDialect
{
DapperDatabaseProvider ProviderType { get; }
string DefaultSchema { get; }
string QuoteIdentifier(string identifier);
string ParameterReference(string name);
string PagingClause(int offset, int size);
bool RequiresOrderByForPaging { get; }
string OrderByFallback { get; }
string NullOrdering(SortOrder order);
string EscapeLikeValue(string value);
string LikeEscapeClause { get; }
bool SupportsTableValuedFunctions { get; }
bool SupportsStoredProcedures { get; }
string BuildSource(string schema, string name, DapperObjectType type,
IReadOnlyList<string> argumentReferences);
string BuildStoredProcedureCall(string schema, string name,
IReadOnlyList<KeyValuePair<string, string>> argumentNames);
}See Dapper: Generated SQL and Dapper: Dialects.
Namespace PepperX.QueryForge.Dapper.Dialects. All sealed, all parameterless, all stateless.
public sealed class SqlServerDialect : ISqlDialect { }
public sealed class PostgreSqlDialect : ISqlDialect { }
public sealed class MySqlDialect : ISqlDialect { }
public sealed class OracleDialect : ISqlDialect { }
public sealed class SqliteDialect : ISqlDialect { }Depends on Microsoft.EntityFrameworkCore 10.0.0.
namespace PepperX.QueryForge.EFCore;
public static class QueryForgeQueryableExtensions
{
public static Task<QueryResult<TModel>> ToQueryResultAsync<TModel>(
this IQueryable<TModel> source, Query query, CancellationToken cancellationToken = default);
public static IQueryable<TModel> ApplyQuery<TModel>(this IQueryable<TModel> source, Query query);
public static IQueryable<TModel> ApplyFilter<TModel>(this IQueryable<TModel> source, Query query);
public static IQueryable<TModel> ApplySort<TModel>(this IQueryable<TModel> source, Query query);
public static IQueryable<TModel> ApplyPaging<TModel>(this IQueryable<TModel> source, Query query);
public static IQueryable<TModel> ApplyProjection<TModel>(
this IQueryable<TModel> source, Query query, IReadOnlyList<string>? alsoKeep = null);
}namespace PepperX.QueryForge.EFCore.Translation;
public static class ExpressionCompiler
{
public static Expression<Func<TModel, bool>>? BuildPredicate<TModel>(QueryCriteria criteria);
public static LambdaExpression? BuildSelector<TModel>(string columnName);
public static Expression<Func<TModel, TModel>>? BuildProjection<TModel>(
IReadOnlyList<string> selectColumns, IReadOnlyList<string>? alsoKeep = null);
public static LambdaExpression? BuildNullRank<TModel>(PropertyInfo property, bool nullsFirst);
public static Expression<Func<TKey, int>>? BuildKeyNullRank<TKey>(bool nullsFirst);
public static PropertyInfo? ResolveProperty<TModel>(string? columnName);
}See EF Core Provider.
No dependencies beyond the core package.
namespace PepperX.QueryForge.InMemory;
public static class InMemoryQueryExtensions
{
public static QueryResult<TModel> ToQueryResult<TModel>(
this IEnumerable<TModel> source, Query query,
Func<TModel, string, object?>? valueAccessor = null);
public static Task<QueryResult<TModel>> ToQueryResultAsync<TModel>(
this IEnumerable<TModel> source, Query query,
Func<TModel, string, object?>? valueAccessor = null,
CancellationToken cancellationToken = default);
public static IEnumerable<TModel> ApplyQuery<TModel>(this IEnumerable<TModel> source, Query query,
Func<TModel, string, object?>? valueAccessor = null);
public static IEnumerable<TModel> ApplyFilter<TModel>(this IEnumerable<TModel> source, Query query,
Func<TModel, string, object?>? valueAccessor = null);
public static IEnumerable<TModel> ApplySort<TModel>(this IEnumerable<TModel> source, Query query,
Func<TModel, string, object?>? valueAccessor = null);
public static IEnumerable<TModel> ApplyPaging<TModel>(this IEnumerable<TModel> source, Query query);
public static IEnumerable<TModel> ApplyProjection<TModel>(this IEnumerable<TModel> source, Query query);
}
public static class InMemoryAccessors
{
public static Func<TRow, string, object?> ForDictionary<TRow>()
where TRow : IReadOnlyDictionary<string, object?>;
public static Func<TModel, string, object?> WithColumnMap<TModel>(
IReadOnlyDictionary<string, string> columnToProperty);
}See In-Memory Provider.
These are internal and are listed only so their names are recognisable in a stack trace. They are
not part of the public contract and can change without a major version.
| Type | Role |
|---|---|
DapperRegistry |
holds one executor per engine; resolves by connection type name |
QueryExecutor |
runs compiled statements and assembles the result |
SchemaCache |
discovers and caches each object's column whitelist |
QueryForgeParameters |
binds parameters and forces BindByName where the driver supports it |
DapperEngine |
holds the registry backing the IDbConnection extension |
DapperQueryService |
the IDapperQueryService implementation |
Test projects have InternalsVisibleTo; applications do not.
QueryForge · part of the PepperX Ecosystem · MIT licensed · packages 2.0.0, .NET 10
Foundations
- Getting Started
- Architecture
- Query Model
- Query Semantics
- Results and Metadata
- JSON Contract
- Fluent Builders
Behaviour
Providers
- Dapper Provider
- Dapper: Generated SQL
- Dapper: Dialects
- EF Core Provider
- EF Core: Joins & Includes
- In-Memory Provider
Practice
Reference