Change Log
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
[Unreleased]
Nothing yet. Add your entry here, under a Keep a Changelog category - ### Added, ### Changed,
### Deprecated, ### Removed, ### Fixed or ### Security - and add the category only when you have
something to put in it. Write a breaking change as - **BREAKING:** ....
The maintainer turns this section into a dated release heading at release time; contributors never bump a version.
[4.0.0] - 2026-08-22
Added
- Native AOT and trimming support. Reference the core package and the database adapter packages and
publish — there is no companion package, no source generator and nothing to opt into. Publishing with
PublishAotorPublishTrimmedreports noIL2xxxorIL3xxxdiagnostic for any supported scenario, and the public API carries neither[RequiresUnreferencedCode]nor[RequiresDynamicCode]. Queries materialize the same results under Native AOT as under the just-in-time compiler — constructor injection (records and other immutable entities), property setters and value tuples of any size all behave identically, down to exception types and messages. The reasoning is recorded in docs/DESIGN-DECISIONS.md. - The libraries now multi-target
net8.0andnet10.0.net8.0remains the supported floor;net10.0is recommended. - The packages are marked trimmable (
[assembly: AssemblyMetadata("IsTrimmable", "True")]), which opts their assemblies into trimming in consuming applications. DataRowimplementsIDynamicMetaObjectProvider, so a row assigned to adynamicreference exposes its columns as members —product.Idreads a column,product.Id = 1assigns one — and constructing and using a row stays warning-free under Native AOT.
Changed
- BREAKING: The non-generic
Query,QueryFirst,QueryFirstOrDefault,QuerySingleandQuerySingleOrDefaultmethods now returnDataRowinstead ofdynamic. Read columns through the string indexer —row["Id"]— which needs no cast and works under Native AOT. Dynamic member access (row.Id) still works wherever the runtime supports dynamic code generation, but the row has to be assigned to adynamicreference first. - BREAKING: Dynamic member access on a
DataRowthrowsKeyNotFoundExceptionfor a column the row does not contain, matching the string indexer. It previously threwRuntimeBinderException. - BREAKING: Through a
dynamicreference, a property on aDataRownow always addresses a column, sorow.Countreads the column namedCountinstead of the row's ownCountproperty. Method calls are unchanged. Use a statically typedDataRowreference to reachCount,KeysandValues. - Behaviour change: materializing a result set whose columns match none of the writable properties of the
target entity type now throws
InvalidOperationExceptioninstead of returning entities with every property left at its default value — a typo in aSELECTalias previously produced a sequence of empty objects with no error. Constructor injection is unaffected; it already failed loudly. - BREAKING: Converting a
Stringto aTimeSpan,DateTimeOffset,DateOnlyorTimeOnlynow parses withCultureInfo.InvariantCultureinstead of the culture of the current thread. The reverse direction already wrote with the invariant culture, so the two halves disagreed: aTimeSpanthis library had itself written as1:2:03:04.567did not parse back at all under a culture whose decimal separator is a comma, and a text column holding03/04/2026read as the 4th of March on anen-USmachine but as the 3rd of April on ade-DEone — silently, with no error. Reading a value out of a database no longer depends on the locale of the machine that runs the code. Applications that stored date and time values in text columns in a culture-specific format have to convert those columns, or read them asStringand parse them themselves. - BREAKING:
EntityPropertyMetadata.PropertyGetterandEntityPropertyMetadata.PropertySetterare now typedFunc<Object, Object?>?andAction<Object, Object?>?instead of Fasterflect'sMemberGetter?andMemberSetter?. The delegate shapes are identical, so code that only invokes an accessor needs no change; code that declares, stores or assigns one has to switch to the BCL delegate types. - The debug representation of a value — used in conversion error messages and in the
ToString()of an SQL statement — no longer goes throughSystem.Text.Json. Sequences are unchanged, so anInt32[]still reads as[1,2,3], but a value of any other type without a dedicated representation is now rendered viaToString(): an entity that previously rendered as{"Enum":3,"Id":1}now renders as, for a record,EntityWithEnumStoredAsString { Enum = Value3, Id = 1 }.
Removed
- The
FasterflectandFastMemberdependencies, both of which emitted code that does not exist under Native AOT. Consuming applications lose two transitive dependencies and the assembly-levelIL2104trim warning that Fasterflect produced when publishing a trimmed or AOT application.
Fixed
- Bumped
Microsoft.Data.Sqliteto 10.0.11 in the SQLite adapter, the first release that resolvesSQLitePCLRaw2.1.12 instead of the vulnerable 2.1.11 (GHSA-2m69-gcr7-jv3q, high severity). Consumers no longer inherit the vulnerable native library.
Migration from 3.x
Reading columns from a non-generic query. Replace dynamic member access with the
DataRowstring indexer —row.Idbecomesrow["Id"]. The indexer needs no cast and works under Native AOT, which is why it is now the documented default.Member access still works wherever the runtime supports dynamic code generation, but the row has to be assigned to a
dynamicreference first, because the static return type isDataRow:dynamic product = connection.QueryFirst($"SELECT * FROM Product WHERE Id = {Parameter(id)}"); var name = product.Name;Two behaviour differences if you do: an unknown column throws
KeyNotFoundExceptionrather thanRuntimeBinderException, and a property always addresses a column, sorow.Countreads the column namedCount. Use a statically typedDataRowreference to reachCount,KeysandValues.EntityPropertyMetadataaccessors. If you readPropertyGetterorPropertySetter— most applications do not — they are nowFunc<Object, Object?>?andAction<Object, Object?>?. Invocation is unchanged; remove anyusing Fasterflect;left behind.A query that maps nothing now throws. If a result set's columns match none of the writable properties of the target type, materialization throws
InvalidOperationExceptioninstead of returning entities left at their defaults. This usually surfaces a pre-existing bug, most often a typo in aSELECTalias.Publishing with Native AOT or trimming. Nothing to install and nothing to opt into: reference the packages and publish.
[3.0.0] - 2026-05-29
Changed
- BREAKING: All NuGet packages have been renamed:
- RentADeveloper.DbConnectionPlus > DbConnectionPlus
- RentADeveloper.DbConnectionPlus.DatabaseAdapters.MySql > DbConnectionPlus.DatabaseAdapters.MySql
- RentADeveloper.DbConnectionPlus.DatabaseAdapters.Oracle > DbConnectionPlus.DatabaseAdapters.Oracle
- RentADeveloper.DbConnectionPlus.DatabaseAdapters.PostgreSql > DbConnectionPlus.DatabaseAdapters.PostgreSql
- RentADeveloper.DbConnectionPlus.DatabaseAdapters.Sqlite > DbConnectionPlus.DatabaseAdapters.Sqlite
- RentADeveloper.DbConnectionPlus.DatabaseAdapters.SqlServer > DbConnectionPlus.DatabaseAdapters.SqlServer
Migration from 2.x
- Remove the 'RentADeveloper.DbConnectionPlus*' packages and add the 'DbConnectionPlus*' packages.
[2.0.0] - 2026-03-21
Changed
- BREAKING: Database adapters have been extracted into separate NuGet packages. Users must now install the adapter package(s) for the database system(s) they use and explicitly register them via
UseXxx()extension methods.RentADeveloper.DbConnectionPlus.DatabaseAdapters.SqlServer—UseSqlServer()RentADeveloper.DbConnectionPlus.DatabaseAdapters.MySql—UseMySql()RentADeveloper.DbConnectionPlus.DatabaseAdapters.PostgreSql—UsePostgreSql()RentADeveloper.DbConnectionPlus.DatabaseAdapters.Oracle—UseOracle()RentADeveloper.DbConnectionPlus.DatabaseAdapters.Sqlite—UseSqlite()
Migration from 1.x
- Install the adapter package(s) for the database(s) you use (see list above).
- Register adapters at application startup:
DbConnectionExtensions.Configure(config => config.UseSqlServer());
[1.2.1] - 2026-03-07
Fixed
- NameHelper.CreateNameFromCallerArgumentExpression stops scanning too early (Fixes issue #7)
[1.2.0] - 2026-02-14
Added
- Optimistic Concurrency Support via Concurrency Tokens (Fixes issue #5)
Changed
- Switched benchmarks to SQLite for more stable results.
[1.1.0] - 2026-02-01
Added
- Fluent configuration API for general settings and entity mappings (Fixes issue #3)
- Support for column name mapping via System.ComponentModel.DataAnnotations.Schema.ColumnAttribute (Fixes issue #1)
- Throw helper for common exceptions
Changed
- Updated all dependencies to latest stable versions
- Refactored unit and integration tests for better maintainability
[1.0.0] - 2026-01-24
Added
- Initial release with comprehensive database support
- Extension methods for
DbConnectionsupporting SQL Server, PostgreSQL, MySQL, SQLite, and Oracle - Interpolated string handler for safe SQL parameterization via
Parameter(value) - On-the-fly temporary table creation via
TemporaryTable(values) - General-purpose methods:
ExecuteNonQuery,ExecuteReader,ExecuteScalar,Exists - Dynamic Query methods:
Query,QueryFirst,QueryFirstOrDefault,QuerySingle,QuerySingleOrDefault - Query methods:
Query<T>,QueryFirst<T>,QueryFirstOrDefault<T>,QuerySingle<T>,QuerySingleOrDefault<T> - Support for mapping to entities, value tuples, scalar values, and dynamic objects
- Entity manipulation methods:
InsertEntity,UpdateEntity,DeleteEntityand batch variants - Enum serialization support with configurable string/integer modes
- Custom database adapter pattern via
IDatabaseAdapterinterface - Full async/await support for all operations