AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql
2.0.0
dotnet add package AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql --version 2.0.0
NuGet\Install-Package AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql -Version 2.0.0
<PackageReference Include="AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql" Version="2.0.0" />
<PackageVersion Include="AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql" Version="2.0.0" />
<PackageReference Include="AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql" />
paket add AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql --version 2.0.0
#r "nuget: AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql, 2.0.0"
#:package AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql@2.0.0
#addin nuget:?package=AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql&version=2.0.0
#tool nuget:?package=AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql&version=2.0.0
π Dapper.GraphQL
β‘ Generate exactly the SQL your GraphQL client asked for β no more, no less.
Dapper.GraphQL bridges Dapper and
GraphQL.NET so the SQL you execute mirrors
the GraphQL selection set: only requested columns are projected, only required
joins are emitted, and Dapper's QueryMultiple machinery materialises the
nested object graph back into your POCOs. π―
π Table of contents
- β¨ Why use it
- π¦ Packages
- β¬οΈ Install
- β‘ Five-minute quickstart
- π§ How it works
- π§© Core concepts
- π§ Advanced usage
- π
DateOnly/TimeOnlyper vendor - π¨ Upgrading from 1.x
- π οΈ Building & testing
- π€ Contributing
- π Credits & license
β¨ Why use it
- π― No over-fetching. Selected GraphQL fields drive
SELECTprojection andJOINemission. - π One round-trip for object graphs. Nested entities load through
Dapper's multi-mapping with
splitOnautomatically configured. - ποΈ Vendor-aware identity retrieval. PostgreSQL
nextval/currval, SQL ServerSCOPE_IDENTITY(), MySQLLAST_INSERT_ID(), SQLitelast_insert_rowid(), Oracle sequences β all behind the sameExecuteWith*Identityextension. - π¨ Strongly typed builders.
SqlBuilder.Insert(person)/SqlBuilder.Update(person)/SqlBuilder.Delete<Person>()derive table and parameter names from your entity types. - π§© First-class DI. Vendor-specific
AddDapperGraphQl<Vendor>extension wires query builders, schemas, and a vendor-awareSqlBuilderOptions(parameter prefix etc.) into your container. - β‘ Performance first. Built on Dapper's lightning-fast micro-ORM β zero reflection at hot path, minimal allocations.
- π¦ Multi-target.
net8.0,net9.0,net10.0,net472,net48,net481.
π¦ Packages
| Package | What's inside |
|---|---|
π§± AdaskoTheBeAsT.Dapper.GraphQL |
Vendor-agnostic core: SqlBuilder, query / insert / update / delete contexts, entity mappers, IQueryBuilder<T>, DI options object. |
π AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql |
AddDapperGraphQlPostgreSql, PostgreSqlIdentity.NextIdentity[Async], ExecuteWithPostgreSqlIdentity[Async]. |
ποΈ AdaskoTheBeAsT.Dapper.GraphQL.SqlServer |
AddDapperGraphQlSqlServer, ExecuteWithSqlServerIdentity[Async]. |
π¬ AdaskoTheBeAsT.Dapper.GraphQL.MySql |
AddDapperGraphQlMySql, ExecuteWithMySqlIdentity[Async]. |
πͺΆ AdaskoTheBeAsT.Dapper.GraphQL.Sqlite |
AddDapperGraphQlSqlite, ExecuteWithSqliteIdentity[Async]. |
π¦
AdaskoTheBeAsT.Dapper.GraphQL.Oracle |
AddDapperGraphQlOracle, OracleIdentity.NextIdentity[Async], ExecuteWithOracleIdentity[Async], BindByNameOracleConnection. |
π‘ Pick exactly one vendor package per database. The vendor package transitively brings the core in.
β¬οΈ Install
dotnet add package AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql
# or .SqlServer / .MySql / .Sqlite / .Oracle
That's it. The vendor package brings in the core. There is no separate
ServiceCollection package β DI lives in each vendor package. β
β‘ Five-minute quickstart
1οΈβ£ Define your entities
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; } = "";
public string LastName { get; set; } = "";
public List<Email> Emails { get; set; } = [];
}
public class Email
{
public int Id { get; set; }
public int PersonId { get; set; }
public string Address { get; set; } = "";
}
2οΈβ£ Write query builders that mirror GraphQL β SQL
public class EmailQueryBuilder : IQueryBuilder<Email>
{
public SqlQueryContext Build(SqlQueryContext query, IHasSelectionSetNode ctx, string alias)
{
query.Select($"{alias}.Id");
query.SplitOn<Email>("Id");
var fields = ctx.GetSelectedFields();
if (fields.ContainsKey("address")) query.Select($"{alias}.Address");
return query;
}
}
public class PersonQueryBuilder(IQueryBuilder<Email> emails) : IQueryBuilder<Person>
{
public SqlQueryContext Build(SqlQueryContext query, IHasSelectionSetNode ctx, string alias)
{
query.Select($"{alias}.Id");
query.SplitOn<Person>("Id");
var fields = ctx.GetSelectedFields();
if (fields.ContainsKey("firstName")) query.Select($"{alias}.FirstName");
if (fields.ContainsKey("lastName")) query.Select($"{alias}.LastName");
if (fields.TryGetValue("emails", out var emailField))
{
query.LeftJoin($"Email email ON {alias}.Id = email.PersonId");
query = emails.Build(query, emailField, "email");
}
return query;
}
}
3οΈβ£ Register everything (vendor-specific DI extension)
services.AddDapperGraphQlPostgreSql(o =>
{
o.AddType<PersonType>();
o.AddType<EmailType>();
o.AddSchema<PersonSchema>();
o.AddQueryBuilder<Person, PersonQueryBuilder>();
o.AddQueryBuilder<Email, EmailQueryBuilder>();
});
For SQL Server use AddDapperGraphQlSqlServer, for MySQL
AddDapperGraphQlMySql, etc. Each one registers the matching
*SqlBuilderOptions (which carries the correct parameter prefix:
@ for PostgreSQL/SQL Server/MySQL/SQLite, : for Oracle).
4οΈβ£ Resolve from a connection wrapped with vendor options
var people = Field<ListGraphType<PersonType>>("people")
.Resolve(ctx =>
{
var query = SqlBuilder
.From("Person person")
.Where("person.IsActive = @isActive", new { isActive = true });
query = personQueryBuilder.Build(query, ctx.FieldAst, "person");
using var raw = new NpgsqlConnection(_connStr);
using var conn = raw.WithDapperGraphQlOptions(_options); // vendor-aware prefix
var mapper = new PersonEntityMapper();
return query.Execute(conn, mapper, ctx.FieldAst);
});
WithDapperGraphQlOptions makes the generated SQL match the connection's
parameter prefix β important on Oracle where parameters use : instead of
@. πͺ
5οΈβ£ Insert with vendor-aware identity retrieval
using AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql.Extensions;
var newId = SqlBuilder
.Insert(person)
.ExecuteWithPostgreSqlIdentity(conn, p => p.Id);
| Vendor | Identity call |
|---|---|
| π PostgreSQL | ExecuteWithPostgreSqlIdentity(conn, p => p.Id) |
| ποΈ SQL Server | ExecuteWithSqlServerIdentity<int>(conn) |
| π¬ MySQL | ExecuteWithMySqlIdentity<int>(conn) |
| πͺΆ SQLite | ExecuteWithSqliteIdentity<int>(conn) |
| π¦ Oracle | ExecuteWithOracleIdentity(conn, p => p.Id) |
π§ How it works
GraphQL request:
{
people {
firstName
emails { address }
}
}
What Dapper.GraphQL runs:
SELECT person.Id, person.FirstName,
email.Id, email.Address
FROM Person person
LEFT JOIN Email email ON person.Id = email.PersonId
WHERE person.IsActive = @isActive;
lastName is never selected because the client never asked for it. βοΈ
Add a phones { number } selection to the GraphQL query and a second join +
columns appear automatically β driven by the matching query builder in DI. π©β¨
The SqlQueryContext.Execute(...) overload uses Dapper's multi-mapping with
the splitOn columns each builder reported via SplitOn<T>(...), then hands
the row tuples to your EntityMapper<T> to assemble the object graph.
π§© Core concepts
SqlBuilder ποΈ
Static fluent factory for the four context types:
SqlBuilder.From("Person p");
SqlBuilder.Insert(person);
SqlBuilder.Update(person);
SqlBuilder.Delete<Person>();
IQueryBuilder<T> π§±
One per entity. Reads the GraphQL selection set, appends columns / joins to a
SqlQueryContext, and returns it. Builders compose: a PersonQueryBuilder
asks an injected IQueryBuilder<Email> to extend the same context.
EntityMapper<T> and DeduplicatingEntityMapper<T> πͺ‘
Multi-mapping turns one logical row into many physical rows once a LEFT JOIN
expands a collection. Entity mappers stitch those rows back into a single
object graph and (with DeduplicatingEntityMapper<T>) collapse duplicate
parent rows.
SqlBuilderOptions and IDapperGraphQlConnection π
Vendor packages register a *SqlBuilderOptions (singleton) that knows the
correct parameter prefix. Wrap your raw DbConnection with
.WithDapperGraphQlOptions(options) so the generated SQL uses the right
prefix for that vendor.
π§ Advanced usage
Chained inserts βοΈ
SqlBuilder
.Insert(person)
.Insert("Email", new { Address = "a@b.com", PersonId = personId })
.Execute(conn);
Async everywhere β±οΈ
await SqlBuilder.Update(person).ExecuteAsync(conn);
await SqlBuilder.Delete<Person>(new { Id = 1 }).ExecuteAsync(conn);
await PostgreSqlIdentity.NextIdentityAsync<Person, int>(conn, p => p.Id);
Self-references and many-to-many πΈοΈ
The integration test suite in
test/integ/AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql.IntegrationTest
demonstrates self-referencing entities (Person.Supervisor,
Person.CareerCounselor) and many-to-many (Person β Company) with composite
join tables β copy those query builders as templates.
Connection pagination (Relay) π
The PostgreSQL test fixture wires up GraphQL.NET v8 ConnectionType<> and
ConnectionType<,> and uses cursor encoding helpers β see GraphQLTests.cs.
π
DateOnly / TimeOnly per vendor
The library targets net472/net48/net481 (where DateOnly does not
exist) and net8.0/net9.0/net10.0 (where it does). Use conditional
compilation in your entities:
public class Person
{
#if NET6_0_OR_GREATER
public DateOnly CreateDate { get; set; }
#else
public DateTime CreateDate { get; set; }
#endif
}
| Provider | DateOnly support |
Action required |
|---|---|---|
| π Npgsql (PostgreSQL) | β Native since 6.0 | None. |
| πͺΆ Microsoft.Data.Sqlite | β Native since 6.0 | None. |
| ποΈ SqlClient (SQL Server) | β οΈ Dapper needs help | Register TypeHandler<DateOnly> etc. |
| π¬ MySqlConnector | β οΈ Dapper needs help; can throw InvalidCastException |
Register custom TypeHandlers. |
| π¦ Oracle | β οΈ Dapper needs help | Register custom TypeHandler<DateOnly>. |
Minimal SQL Server / MySQL / Oracle handler:
public sealed class DapperDateOnlyHandler : SqlMapper.TypeHandler<DateOnly>
{
public override DateOnly Parse(object value) => DateOnly.FromDateTime((DateTime)value);
public override void SetValue(IDbDataParameter parameter, DateOnly value)
{
parameter.DbType = DbType.Date;
parameter.Value = value.ToDateTime(TimeOnly.MinValue);
}
}
SqlMapper.AddTypeHandler(new DapperDateOnlyHandler());
π‘ Tip. Always use strongly typed Dapper queries (
Query<Person>(...)) β dynamic queries can returnDateTimeeven whereDateOnlyis registered.
π¨ Upgrading from 1.x
2.0.0 splits the previously monolithic AdaskoTheBeAsT.Dapper.GraphQL
package into a vendor-agnostic core plus five vendor-specific packages, and
removes the standalone AdaskoTheBeAsT.Dapper.GraphQL.ServiceCollection
package β DI moves into each vendor package.
π See MIGRATION.md for the full upgrade guide, namespace mapping, and copy-pasteable before/after snippets.
TL;DR:
- π Replace
AdaskoTheBeAsT.Dapper.GraphQL.ServiceCollectionwith the matching vendor package (*.PostgreSql,*.SqlServer,*.MySql,*.Sqlite,*.Oracle). - π Replace
services.AddDapperGraphQL(...)withservices.AddDapperGraphQl<Vendor>(...). - π Replace
using AdaskoTheBeAsT.Dapper.GraphQL.Extensions;with the vendor namespace (e.g.AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql.Extensions). - π Rename
ExecuteWithSqlIdentityβExecuteWithSqlServerIdentity.
If you only used the vendor-agnostic surface (SqlBuilder,
SqlQueryContext, EntityMapper) bumping the package version is enough. β
π οΈ Building & testing
Prerequisites
- π£ .NET 10 SDK
- π³ Docker β required by the integration test suites; each fixture spins up its own container via Testcontainers for PostgreSQL / SQL Server / MySQL / Oracle. SQLite uses an in-memory file.
Run everything
dotnet restore
dotnet build -c Release
dotnet test -c Release
Run a single integration suite
dotnet test test/integ/AdaskoTheBeAsT.Dapper.GraphQL.PostgreSql.IntegrationTest
π€ Contributing
PRs welcome! Please:
- π¬ Open an issue first if you're proposing a behaviour change or new public API.
- β Add / extend integration tests for the affected vendor(s).
- π§Ή Keep
dotnet build /warnaserrorclean β the repo treats warnings as errors.
Standard fork β branch β PR flow:
git checkout -b feature/amazing-thing
git commit -m "feat: amazing thing"
git push origin feature/amazing-thing
π Credits & license
Originally created by the Landmark Home Warranty team:
- Doug Day
- Kevin Russon
- Ben McCallum
- Natalya Arbit
- Per Liedman
- John Stovin
Maintained by @AdaskoTheBeAsT. π
Released under the MIT License. π
π Links
- π¦ NuGet β core
- π NuGet β PostgreSQL
- ποΈ NuGet β SQL Server
- π¬ NuGet β MySQL
- πͺΆ NuGet β SQLite
- π¦ NuGet β Oracle
- π Dapper
- π GraphQL.NET
Made with β€οΈ for developers who love clean code and fast queries.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Framework | net472 is compatible. net48 is compatible. net481 is compatible. |
-
.NETFramework 4.7.2
- AdaskoTheBeAsT.Dapper.GraphQL (>= 2.0.0)
-
.NETFramework 4.8
- AdaskoTheBeAsT.Dapper.GraphQL (>= 2.0.0)
-
.NETFramework 4.8.1
- AdaskoTheBeAsT.Dapper.GraphQL (>= 2.0.0)
-
net10.0
- AdaskoTheBeAsT.Dapper.GraphQL (>= 2.0.0)
-
net8.0
- AdaskoTheBeAsT.Dapper.GraphQL (>= 2.0.0)
-
net9.0
- AdaskoTheBeAsT.Dapper.GraphQL (>= 2.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0 | 90 | 5/18/2026 |