FclEx.Dapper
2.3.4
dotnet add package FclEx.Dapper --version 2.3.4
NuGet\Install-Package FclEx.Dapper -Version 2.3.4
<PackageReference Include="FclEx.Dapper" Version="2.3.4" />
<PackageVersion Include="FclEx.Dapper" Version="2.3.4" />
<PackageReference Include="FclEx.Dapper" />
paket add FclEx.Dapper --version 2.3.4
#r "nuget: FclEx.Dapper, 2.3.4"
#:package FclEx.Dapper@2.3.4
#addin nuget:?package=FclEx.Dapper&version=2.3.4
#tool nuget:?package=FclEx.Dapper&version=2.3.4
FclEx.Dapper
FclEx.Dapper adds focused, cache-aware CRUD and transaction helpers to Dapper while keeping connections, transactions, SQL providers, and execution boundaries visible to the caller. It is not an ORM: it does not provide change tracking, relationship management, LINQ translation, schema migrations, repositories, or a unit of work.
Installation
Install FclEx.Dapper and the ADO.NET provider used by the application:
dotnet add package FclEx.Dapper
dotnet add package Microsoft.Data.Sqlite
The package targets net472, netstandard2.0, net8.0, net9.0, and net10.0. It references Dapper and FclEx.Core, but it does not add a database provider dependency.
Quick Start
Map an entity with DataAnnotations:
[Table("widgets")]
public sealed class Widget
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string Name { get; set; } = "";
}
Use a normal provider connection. The schema must already exist; the CREATE TABLE below is included only to make the example runnable:
using var connection = new SqliteConnection("Data Source=:memory:");
await connection.OpenAsync();
await connection.ExecuteAsync(
"CREATE TABLE widgets (Id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT NOT NULL)");
var id = await connection.InsertAsync(new Widget { Name = "first" });
var widget = await connection.GetAsync<Widget>(id);
await connection.BulkInsertAsync(
[
new Widget { Name = "second" },
new Widget { Name = "third" },
]);
await connection.DeleteAsync<Widget>(id);
InsertAsync<TEntity> returns the generated key as long. Use InsertAsync<TEntity, TKey> when the generated key has another type.
Providers and SQL Adapters
FclEx.Dapper recognizes these provider connection types when the corresponding provider package is installed:
| Database | Provider package | Built-in adapter | Generated-key SQL |
|---|---|---|---|
| SQL Server | Microsoft.Data.SqlClient |
SqlServerAdapter |
OUTPUT INSERTED |
| PostgreSQL | Npgsql |
NpgsqlAdapter |
RETURNING |
| SQLite | Microsoft.Data.Sqlite |
SqliteAdapter |
last_insert_rowid() |
| MySQL | MySql.Data |
MySqlAdapter |
LAST_INSERT_ID() |
| MySQL/MariaDB | MySqlConnector |
MySqlConnectorAdapter |
LAST_INSERT_ID() |
Adapter resolution examines the runtime connection type and its base types. Register an adapter for a custom or wrapped connection type when it cannot be recognized automatically:
DapperHelper.RegisterSqlAdapter<CustomConnection>(new CustomSqlAdapter()); // add or replace
var added = DapperHelper.TryRegisterSqlAdapter<CustomConnection>(new CustomSqlAdapter());
TryRegisterSqlAdapter returns false when the exact connection type is already registered. A registered adapter must keep its SQL-affecting behavior stable because generated SQL is cached by adapter instance. Schema support follows ISqlAdapter.SupportsSchemas; unsupported adapters ignore mapped and per-call schemas.
Table, schema, and column names supplied through mappings or method arguments must come from trusted application configuration, not end-user input. Adapters quote and escape identifiers, but identifier names cannot be parameterized like data values.
Entity Mapping and Key Limits
DataAnnotationsEntityMappingSource is used by default. It supports:
[Table], includingSchema[Column], including column aliases and provider store type names[Key][NotMapped][DatabaseGenerated]withNone,Identity, orComputed
By convention, public readable and writable scalar instance properties are persistent. Static properties, indexers, read-only properties, navigation properties, and explicitly unmapped properties are excluded. A non-scalar property must declare an explicit mapping attribute to be included.
GetAsync<T> and DeleteAsync<T> require exactly one mapped key. Generated-key return from InsertAsync requires exactly one generated key. Composite-key lookup and deletion are not supported.
Implement IEntityMappingSource when mappings should be independent of DataAnnotations. A source must return the same immutable EntityMapping instance whenever the same entity type is requested because mapping identity participates in SQL cache keys:
var mapping = new EntityMapping(
typeof(Widget),
"widgets",
[
new(typeof(Widget).GetProperty(nameof(Widget.Id))!, "Id", true,
DatabaseValueGeneration.OnInsert),
new(typeof(Widget).GetProperty(nameof(Widget.Name))!, "Name"),
]);
var options = new CommandOptions { EntityMappingSource = new ApplicationMappingSource(mapping) };
var id = await connection.InsertAsync(new Widget { Name = "mapped" }, commandOptions: options);
ApplicationMappingSource in this example is an application-owned IEntityMappingSource implementation.
Commands, Connections, and Transactions
CommandOptions carries the command timeout, local transaction, adapter override, mapping source, and cancellation token. The same options shape is accepted by connection and transaction CRUD methods.
CRUD helpers record the connection's initial state. A connection opened by the helper is closed before the operation returns; a connection supplied already open remains open. Transaction extension methods bind the receiver transaction to the generated command.
ExecuteInTransactionAsync starts a local transaction with ReadCommitted by default, commits after a successful callback, and attempts rollback after callback or commit failure. If both the operation and rollback fail, an AggregateException contains both exceptions.
Bulk Inserts and SQL Caching
BulkInsertAsync emits bounded multi-row INSERT commands; it does not silently execute one command per entity. A batch contains at most 500 rows and may be smaller because of provider row or parameter limits. Multiple rows with no insertable properties are rejected when the adapter cannot express an efficient bulk form.
Canonical CRUD command text is cached by adapter instance, immutable mapping identity, operation shape, and batch row count. Per-call schema and adapter overrides do not enter the process-wide cache. This avoids repeated SQL string construction without permanently retaining open-ended override values.
Explicit Generated Keys
Use InsertWithExplicitGeneratedKeysAsync or BulkInsertAsync(..., includeAutoKey: true) only when importing values for keys normally generated by the database.
These operations do not advance or reset provider identity, sequence, or auto-increment state. The caller must maintain that state so later database-generated keys do not conflict with explicitly inserted values.
Dapper Global State and Type Handlers
Core CRUD operations do not scan assemblies or modify Dapper's process-wide type maps, type handlers, or settings. Generated queries alias database columns back to CLR property names, so they do not require a global Dapper type map.
Dapper.GuidTypeHandler and Dapper.AssumeUtcDateTimeTypeHandler are optional helpers. Registering either through SqlMapper.AddTypeHandler changes Dapper process-wide state and remains the application's responsibility.
See DESIGN.md for the principles governing future changes.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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 Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.7.2
- Dapper (>= 2.1.79)
- FclEx.Core (>= 2.3.4)
-
.NETStandard 2.0
- Dapper (>= 2.1.79)
- FclEx.Core (>= 2.3.4)
-
net10.0
- Dapper (>= 2.1.79)
- FclEx.Core (>= 2.3.4)
-
net8.0
- Dapper (>= 2.1.79)
- FclEx.Core (>= 2.3.4)
-
net9.0
- Dapper (>= 2.1.79)
- FclEx.Core (>= 2.3.4)
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.3.4 | 90 | 8/28/2026 |
| 2.3.3 | 99 | 8/22/2026 |
| 2.3.2 | 99 | 8/20/2026 |
| 2.3.1 | 93 | 8/19/2026 |
| 2.3.0 | 102 | 8/17/2026 |
| 2.2.9 | 115 | 7/18/2026 |
| 2.2.8 | 125 | 6/13/2026 |
| 2.2.6 | 118 | 6/8/2026 |
| 2.2.5 | 117 | 6/5/2026 |
| 2.2.4 | 123 | 6/5/2026 |
| 2.2.3 | 124 | 5/27/2026 |
| 2.2.2 | 120 | 5/26/2026 |
| 2.2.1 | 120 | 5/23/2026 |
| 2.2.0 | 119 | 5/13/2026 |
| 2.1.5 | 113 | 5/11/2026 |
| 2.1.4 | 119 | 4/18/2026 |
| 2.1.3 | 128 | 4/6/2026 |
| 2.1.2 | 133 | 4/6/2026 |
| 2.1.1 | 132 | 4/2/2026 |
| 1.0.0 | 123 | 5/14/2026 |