Summary
DbConnectionPlus is currently not Native AOT compatible. Its mapping engine relies on runtime code generation (expression-tree .Compile() + MakeGenericMethod over value types, FastMember's Reflection.Emit, Fasterflect's DynamicMethod), all of which fail or warn under PublishAot=true. Under AOT, Query<T> (entities/value tuples), entity insert/update/delete, and temporary tables fail at runtime.
This request is to add AOT support via an opt-in companion package (DbConnectionPlus.Aot), following the Dapper → DapperAOT model, plus a small core change that makes untyped-row access AOT-safe by default.
Goals
- Full feature parity off AOT. Every DbConnectionPlus feature must remain usable without the companion package and under a normal (JIT) runtime — this is the complete, unrestricted experience.
- Maximum feature coverage on AOT. With the companion package referenced and opted in, as many features as possible must work under
PublishAot=true - target: Query<T> (entities/value tuples/scalars), entity CRUD, and temporary tables.
- No awkward tax on non-AOT consumers. AOT support must not push API changes that are cumbersome, weird, or surprising for people who never use AOT.
Requirements
-
Companion NuGet package. Ship AOT support as a separate opt-in package (DbConnectionPlus.Aot) - a source generator that supplies reflection-free code paths for AOT consumers. Referencing it and adopting the required toolchain is opt-in; JIT-only users need neither.
-
Non-generic query methods return DataRow instead of dynamic. Change the non-generic Query() / QueryFirst() / QuerySingle() / … overloads to return the statically-typed DataRow (a reflection-free type that is AOT-safe to produce). Its string indexer (row["Id"]) then works under AOT by default, with no cast. Dynamic member access (row.Id) stays available on the JIT as an explicit opt-in by assigning the result to dynamic. This is a breaking change, which is acceptable given the project's small consumer base.
-
Breaking API changes are permitted where they help. Backward compatibility is not a hard constraint. Breaking changes are acceptable when they simplify the implementation or make AOT usage more convenient - provided they do not degrade the non-AOT experience (Goal 3) or remove features from the non-AOT path (Goal 1).
-
Analyzer warnings for AOT-incompatible API usage. Provide build-time diagnostics that flag usage of API parts that cannot be made AOT-safe - in particular dynamic member access on a dynamic-typed row (row.Id), which is a DLR call site that no generator can fix. Diagnostics should steer AOT users toward the AOT-safe alternatives: Query<T>, or the DataRow string indexer (row["Id"]).
-
Documentation of the dynamic opt-in. Update the README and the XML doc comments on the affected methods to explain that they now return DataRow, that row["Id"] is the recommended (and AOT-safe) access, and that dynamic member access requires opting in via dynamic row = ... (JIT-only).
Known bounds (documented, not in scope to fix)
- Dynamic member access stays JIT-only by nature; it is surfaced via diagnostics and documentation, not made AOT-compatible.
- End-to-end AOT is also gated by the ADO.NET provider: SQLite, MySQL, and PostgreSQL are viable; SQL Server works for the library's mainline paths (with provider caveats for Entra ID / Always Encrypted); Oracle remains blocked by its provider.
Acceptance criteria
- All features remain available and behave correctly without the companion package on the JIT.
- Non-generic query methods return
DataRow; row["Id"] works under PublishAot=true with no cast, and dynamic row = ... still enables member access on the JIT.
- With the companion package referenced and opted in,
Query<T>, entity CRUD, and temporary tables succeed under PublishAot=true.
- Building/publishing an AOT consumer produces actionable analyzer warnings for AOT-incompatible API usage.
- README and XML doc comments document the
DataRow return type, indexer access, and the dynamic opt-in.
- Any API changes introduced for AOT impose no cumbersome or surprising burden on non-AOT consumers.
Summary
DbConnectionPlus is currently not Native AOT compatible. Its mapping engine relies on runtime code generation (expression-tree
.Compile()+MakeGenericMethodover value types, FastMember'sReflection.Emit, Fasterflect'sDynamicMethod), all of which fail or warn underPublishAot=true. Under AOT,Query<T>(entities/value tuples), entity insert/update/delete, and temporary tables fail at runtime.This request is to add AOT support via an opt-in companion package (
DbConnectionPlus.Aot), following the Dapper → DapperAOT model, plus a small core change that makes untyped-row access AOT-safe by default.Goals
PublishAot=true- target:Query<T>(entities/value tuples/scalars), entity CRUD, and temporary tables.Requirements
Companion NuGet package. Ship AOT support as a separate opt-in package (
DbConnectionPlus.Aot) - a source generator that supplies reflection-free code paths for AOT consumers. Referencing it and adopting the required toolchain is opt-in; JIT-only users need neither.Non-generic query methods return
DataRowinstead ofdynamic. Change the non-genericQuery()/QueryFirst()/QuerySingle()/ … overloads to return the statically-typedDataRow(a reflection-free type that is AOT-safe to produce). Its string indexer (row["Id"]) then works under AOT by default, with no cast. Dynamic member access (row.Id) stays available on the JIT as an explicit opt-in by assigning the result todynamic. This is a breaking change, which is acceptable given the project's small consumer base.Breaking API changes are permitted where they help. Backward compatibility is not a hard constraint. Breaking changes are acceptable when they simplify the implementation or make AOT usage more convenient - provided they do not degrade the non-AOT experience (Goal 3) or remove features from the non-AOT path (Goal 1).
Analyzer warnings for AOT-incompatible API usage. Provide build-time diagnostics that flag usage of API parts that cannot be made AOT-safe - in particular dynamic member access on a
dynamic-typed row (row.Id), which is a DLR call site that no generator can fix. Diagnostics should steer AOT users toward the AOT-safe alternatives:Query<T>, or theDataRowstring indexer (row["Id"]).Documentation of the
dynamicopt-in. Update the README and the XML doc comments on the affected methods to explain that they now returnDataRow, thatrow["Id"]is the recommended (and AOT-safe) access, and that dynamic member access requires opting in viadynamic row = ...(JIT-only).Known bounds (documented, not in scope to fix)
Acceptance criteria
DataRow;row["Id"]works underPublishAot=truewith no cast, anddynamic row = ...still enables member access on the JIT.Query<T>, entity CRUD, and temporary tables succeed underPublishAot=true.DataRowreturn type, indexer access, and thedynamicopt-in.