Skip to content
AmirHosseinMp02 edited this page Aug 2, 2026 · 2 revisions

FAQ


Which package do I install?

The one for how you fetch data. The core comes along with any of them.

  • Raw SQL over a relational database → PepperX.QueryForge.Dapper
  • You already have a DbContextPepperX.QueryForge.EFCore
  • A list you already hold → PepperX.QueryForge.InMemory

Installing more than one is normal. The sample uses all three.

Can I use the Dapper and EF Core providers in the same application?

Yes, including against the same table. They return the same QueryResult<T> and are asserted against each other in the parity suite.

Does it work with .NET 8 or 9?

No. All four packages target .NET 10 only.

Is there a version that does writes?

No. QueryForge is a read engine — there is no insert, update or delete surface, and none is planned.

Can I join tables?

Not in the query model — a query targets one object — but you compose the join before handing the query over, which works fully. With EF Core, project the join into a named DTO and the joined columns become filterable, sortable and groupable like any other. Full treatment, including Include and AsSplitQuery, in EF Core: Joins and Includes.

await db.Orders
    .Select(o => new OrderRow { Id = o.Id, CustomerName = o.Customer.Name, /* … */ })
    .ToQueryResultAsync<OrderRow>(query);

QueryForge then filters and sorts the flat shape.

Can I filter on a navigation property, like "Customer.Name"?

Dotted paths do not resolve at all. The navigation itself ("Customer") does resolve as a filter — Equals against null is a valid "has a related row" test — but it is never sortable or groupable. Project to a flat DTO to query joined columns; see EF Core: Joins and Includes.

Why did my Include stop returning related data when I sent selectColumns?

It no longer does. EF Core drops every Include from a query carrying a projection, so QueryForge detects the include and skips the projection instead — selectColumns is ignored on such a query rather than the included data being silently lost. If you need both a narrowed payload and related data, project the join into a flat DTO. See EF Core: Joins and Includes.

Can I aggregate — SUM, AVG, MAX?

Not in the model. Grouping produces counts and nested rows. For aggregates, put them in a view and query that, or compute them from a grouped result if the rows are already in hand.


Why does my grouped query return so much data?

Because page size bounds the number of groups, and every row under those groups comes back so that Count can be truthful. Filter before grouping, or group by a higher-cardinality column. Full explanation: Grouping and Hierarchies.

Why is Meta.Total.Rows small on a grouped query?

On a grouped result it counts distinct outermost group keys, not rows — because that is what is being paged. See Results and Metadata.

Why is my filter being ignored instead of returning nothing?

By design. An input QueryForge cannot honour is dropped, never turned into a match-nothing clause — a search box the user left blank means "no constraint", not "no results".

The checklist for finding out which rule dropped it is in Error Reference.

Why did my query not throw when I asked for a column that does not exist?

Same reason, plus a security one: an unknown column name is dropped silently so a caller cannot use error responses to discover your schema. See Security.

Rows appear on two pages, or disappear between pages

Your sort does not uniquely determine an order, so the database is free to break ties differently between calls. Add a unique tie-breaker:

.Sort(new SortDescriptor("Score", SortOrder.Descending), new SortDescriptor("UserId"))

Results differ between the in-memory provider and the database

Almost always string case. In-memory comparison is OrdinalIgnoreCase; a database follows its column's collation. The full list of divergences is in Cross-Provider Parity.


Is it safe to accept a Query straight from a browser?

The structural protections are automatic — values are parameters, unknown columns are dropped, and a client cannot choose the target. What you must add is policy: which real columns a caller may reach, and how large a page may be. That is Validation, and it is not optional on a public endpoint.

Can a client choose which table is queried?

Not through the model. Query has no object field; only DapperQuery does, and you set it. Bind the body as Query and upgrade with DapperQueryBuilder.FromBase(...).

If you genuinely need a client-selected dataset, map an allow-listed key to a name in your own code — never pass the client's string through.

I denied a column in Select but callers can still filter on it

The five rule sets are independent by design. Deny it in all four places — Select, Where, Sort and GroupBy — or, better, point the endpoint at a view that does not contain the column at all.

Does QueryForge protect against SQL injection?

Yes, on both axes. Values are always DbParameters and never concatenated. Column names are looked up against the target's real columns and dropped if absent, then quoted by the dialect. Details: Security.


Does it need any permissions on my database?

SELECT on the objects you point it at. Nothing is deployed, migrated or created — that changed in 2.0.

How many round trips does a query cost?

Two for a flat query (a count and a page), three for a grouped one, one for a stored procedure. Plus a one-off schema probe the first time an object is seen, ever.

Is the schema cached? What if I add a column?

Cached per provider | schema | name | type for the lifetime of the process, with no invalidation API. Restart the application after changing a table's shape, or new columns will be dropped from queries.

Does it support AsNoTracking?

That is EF Core's, and it composes normally:

await db.Users.AsNoTracking().ToQueryResultAsync<User>(query);

Projected queries are untracked regardless, because EF Core does not track instances constructed inside a projection.

Can I see the SQL?

EF Core:

db.Users.ApplyQuery(query).ToQueryString();

Dapper — the compiler is public, so no database is needed:

new SqlQueryCompiler(new PostgreSqlDialect()).CompileRows(dapperQuery, whitelist).Text;

See Dapper: Generated SQL.


Can I add a database engine?

Implement ISqlDialect — thirteen members — and register it. The caveat is that DapperDatabaseProvider is a closed enum, so an engine outside the five has no member of its own today. Options and trade-offs: Extending QueryForge.

Can I override a built-in dialect?

Yes. Register one with the same ProviderType and it replaces the built-in. The built-ins are sealed, so implement ISqlDialect and delegate for what you are not changing.

Can I use it with MongoDB / Elasticsearch / a REST API?

Not out of the box, but the extension point exists — write a provider. The core exposes the shared semantics so yours agrees with the others, and the two shared test suites tell you when it does. See Extending QueryForge.

Can I query dictionaries or dynamic rows?

Yes, in-memory, with a value accessor:

rows.ToQueryResult(query, InMemoryAccessors.ForDictionary<Dictionary<string, object?>>());

Note this switches off column-existence checking. See In-Memory Provider.

My model's property names differ from the column names

var accessor = InMemoryAccessors.WithColumnMap<User>(new Dictionary<string, string>
{
    ["name"] = nameof(User.FirstName)
});

For the database providers, alias the columns in a view so the names the caller uses are the names the target exposes.


Which grids does it work with?

Any that send filter, sort, group and paging descriptors — DevExtreme, AG Grid, Kendo UI. The hierarchy shape is deliberately key / count / items, which is what those grids expect. Mappers: Recipes.

Can I use it without ASP.NET Core?

Yes. Nothing depends on the web stack. The Dapper provider takes IServiceCollection for registration, and that package is a dependency of the provider anyway — but you can also skip DI entirely and use connection.QueryForgeAsync<T>(query).

How do I test code that uses QueryForge?

Validation and query construction are pure and need nothing. For semantics, run the same query through the in-memory provider against seed data — it implements the same rules, so the test is meaningful about the database. See Testing.

Is it thread-safe?

The registry, executors, schema caches, compilers and dialects are all safe for concurrent use. IDbConnection is not — that is ADO.NET's rule. Do not share one connection across concurrent calls.


Where do I report a bug or ask for a feature?

https://github.com/PepperX-Dev/QueryForge/issues. For a query returning the wrong rows, include the Query as JSON, the provider and engine, and the SQL if you can get it.

For a security issue, open a security advisory rather than a public issue.

What is the licence?

MIT, for all four packages.

Clone this wiki locally