Skip to content

Database Providers

DapperMatic supports multiple database providers, each with their own connection types and specific features. This page covers the supported providers and their capabilities.

Quick Navigation

Supported Providers

SQL Server

Supported Versions: SQL Server 2017+, 2019, 2022
Connection Packages: Microsoft.Data.SqlClient or System.Data.SqlClient
Schema Support: Full schema support with dbo as default

csharp
using Microsoft.Data.SqlClient;

var connectionString = "Server=localhost;Database=MyApp;Integrated Security=true;";
using var connection = new SqlConnection(connectionString);

// SQL Server uses schemas extensively
var table = new DmTable("dbo", "TableName", columns);
await connection.CreateTableIfNotExistsAsync(table);

Key Features:

  • Full DDL support including all constraint types
  • Auto-increment columns via IDENTITY
  • Rich data type support
  • Computed columns and check constraints
  • Filtered indexes and included columns

Limitations:

  • Schema name required for most operations
  • Some DDL operations require elevated permissions

MySQL / MariaDB

Supported Versions: MySQL 5.7+, 8.4, 9.0; MariaDB 10.11+, 11.1
Connection Packages: MySqlConnector or MySql.Data
Schema Support: Database-level organization (no schemas)

csharp
using MySqlConnector;

var connectionString = "Server=localhost;Database=myapp;Uid=user;Pwd=password;";
using var connection = new MySqlConnection(connectionString);

// MySQL doesn't use schemas - pass null or database name
var table = new DmTable(null /* or databaseName */, "TableName", columns);
await connection.CreateTableIfNotExistsAsync(table);

Key Features:

  • Auto-increment columns via AUTO_INCREMENT
  • Full-text search indexes
  • Spatial data types and functions
  • JSON column type support
  • Partitioning support

Limitations:

  • No schema concept (database = schema)
  • Limited check constraint support (MySQL 8.0.16+)
  • Some DDL operations don't support transactions

PostgreSQL

Supported Versions: PostgreSQL 15+, 16+ (with optional PostGIS extension support)
Connection Package: Npgsql
Schema Support: Full schema support with public as default

csharp
using Npgsql;

var connectionString = "Host=localhost;Database=myapp;Username=user;Password=password;";
using var connection = new NpgsqlConnection(connectionString);

// PostgreSQL is case-sensitive for quoted identifiers
var table = new DmTable("public", "TableName", columns);
await connection.CreateTableIfNotExistsAsync(table);

Key Features:

  • Advanced data types (arrays, JSON, UUID, etc.)
  • Full ACID compliance
  • Excellent performance with large datasets
  • Rich indexing options (GiST, GIN, SP-GiST, BRIN)
  • Native array support in DapperMatic

Limitations:

  • Case-sensitive for quoted identifiers
  • Some advanced features may not be portable

SQLite

Supported Versions: SQLite 3.35+
Connection Package: Microsoft.Data.Sqlite or System.Data.SQLite
Schema Support: Single database file (no schemas)

csharp
using Microsoft.Data.Sqlite;

var connectionString = "Data Source=myapp.db";
using var connection = new SqliteConnection(connectionString);

// SQLite doesn't use schemas
var table = new DmTable(null /* no schema support */, "TableName", columns);
await connection.CreateTableIfNotExistsAsync(table);

Key Features:

  • Zero-configuration embedded database
  • Full ACID compliance
  • Cross-platform compatibility
  • JSON support (SQLite 3.38+)
  • Excellent for development and testing

Limitations:

  • Limited ALTER TABLE support - DapperMatic overcomes this automatically
  • No native date/time types (stored as TEXT/INTEGER)
  • Single writer at a time
  • No schemas or stored procedures

How DapperMatic Handles SQLite Limitations:

SQLite's ALTER TABLE statement has significant restrictions - it cannot modify existing columns, drop columns, or change column types. DapperMatic automatically works around these limitations using a sophisticated "table recreation strategy":

  1. Automatic Detection: When a column operation would fail in SQLite, DapperMatic detects this and switches to recreation mode
  2. Data Preservation: Creates a temporary table with existing data before making schema changes
  3. Schema Recreation: Drops and recreates the table with the new schema structure
  4. Intelligent Data Migration: Copies compatible data from the temporary table to the new table
  5. Transaction Safety: The entire process runs within a transaction for atomicity
  6. Foreign Key Handling: Temporarily disables foreign key constraints during recreation

This means operations like DropColumnIfExistsAsync(), CreateColumnIfNotExistsAsync(), and constraint modifications work seamlessly in SQLite, just like other providers - you don't need to worry about SQLite's native limitations.

Custom Providers

DapperMatic's extensible architecture allows you to work with custom database providers and connection wrappers. Many third-party libraries provide IDbConnection implementations that wrap existing providers to add functionality like profiling, caching, logging, or custom connection pooling.

Wrapper Providers (Easiest)

These are the most common custom providers - they wrap existing database connections to add functionality while delegating core database operations to the underlying provider.

MiniProfiler Integration

Use Case: Add SQL profiling and performance monitoring to your DDL operations.

csharp
using StackExchange.Profiling.Data;
using Microsoft.Data.SqlClient;

// Create a factory that recognizes the wrapper
public class ProfiledSqlServerMethodsFactory : Providers.SqlServer.SqlServerMethodsFactory
{
    public override bool SupportsConnectionCustom(IDbConnection db) =>
        db is ProfiledDbConnection pdc && pdc.InnerConnection is SqlConnection;
}

// Register the factory
DatabaseMethodsProvider.RegisterFactory(
    // any unique name will do
    "ProfiledDbConnection.SqlServer",
    new ProfiledSqlServerMethodsFactory());

// Wrap your connection with MiniProfiler
var baseConnection = new SqlConnection(connectionString);
var profiledConnection = new ProfiledDbConnection(baseConnection, MiniProfiler.Current);

// Now your profiled connection works with DapperMatic
var table = new DmTable(null /* or schemaName */, "my_table", new[] {
    new DmColumn("id", typeof(int)) { IsPrimaryKey = true },
    new DmColumn("name", typeof(string)),
});
await profiledConnection.CreateTableIfNotExistsAsync(table);

ServiceStack OrmLite Integration

Use Case: Use DapperMatic DDL operations with ServiceStack's OrmLite connections.

csharp
using ServiceStack.OrmLite;
using ServiceStack.OrmLite.Sqlite;

// Create a factory for OrmLite SQLite connections
public class OrmLiteSqliteDialectMethodsFactory : SqliteMethodsFactory
{
	public override bool SupportsConnectionCustom(IDbConnection db)
	{
		// OrmLite connections implement IDbConnection directly
		return db is OrmLiteConnection odb && odb.DialectProvider is SqliteOrmLiteDialectProvider; // SQLite check
	}
}

// Register the factory
DatabaseMethodsProvider.RegisterFactory(
    nameof(OrmLiteSqliteDialectMethodsFactory),
    new OrmLiteSqliteDialectMethodsFactory());

// Create OrmLite connection
var sqliteFile = Path.GetTempFileName();
var factory = new OrmLiteConnectionFactory(sqliteFile, SqliteDialect.Provider);
using (var connection = factory.OpenDbConnection())
{
    var table = new DmTable(null /* or schemaName */, "my_table", new[] {
        new DmColumn("id", typeof(int)) { IsPrimaryKey = true },
        new DmColumn("name", typeof(string)),
    });
    await connection.CreateSchemaIfNotExistsAsync("reporting"); // returns false with SQLite
    await connection.CreateTableIfNotExistsAsync(table);

    var dbTable = await connection.GetTableAsync(null, "my_table");
    if (dbTable != null) Console.WriteLine("Table exists with columns: " + string.Join(", ", dbTable.Columns.Select(c => c.ColumnName)));

    // Prints: Table exists with columns: id, name
}

if (File.Exists(sqliteFile)) File.Delete(sqliteFile);

Resilience and Retry Logic

Use Case: Add automatic retry logic for transient database failures.

csharp
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;
using Polly;

public class ResilientDbConnection : IDbConnection
{
    private readonly IDbConnection _innerConnection;
    private readonly IAsyncPolicy _retryPolicy;

    public ResilientDbConnection(IDbConnection innerConnection, ILogger logger)
    {
        _innerConnection = innerConnection;
        _retryPolicy = Policy
            .Handle<SqlException>(ex => IsTransientError(ex.Number))
            .WaitAndRetryAsync(3, retryAttempt =>
                TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetry: (outcome, timespan, retryCount, context) =>
                {
                    logger.LogWarning("Retrying database operation. Attempt {RetryCount}", retryCount);
                });
    }

    // Implement IDbConnection by delegating to _innerConnection
    // with retry policy applied to operations
}

public class ResilientSqlServerMethodsFactory : SqlServerMethodsFactory
{
    public override bool SupportsConnection(IDbConnection db)
    {
        return db is ResilientDbConnection rdc && rdc.InnerConnection is SqlConnection;
    }
}

// Usage
var resilientConnection = new ResilientDbConnection(baseConnection, logger);
DatabaseMethodsProvider.RegisterFactory(
    "ResilientDbConnection.SqlServer",
    new ResilientSqlServerMethodsFactory());

// DDL operations now have automatic retry logic
var criticalTable = new DmTable("dbo", "CriticalTable", criticalColumns);
await resilientConnection.CreateTableIfNotExistsAsync(criticalTable);

Native Custom Providers (Advanced)

For completely new database engines or significantly different connection implementations, you'll need to implement the full provider interface.

Custom Database Engine

Use Case: Supporting a proprietary or emerging database that doesn't have existing .NET providers.

csharp
// Example: Fictional CentipedeDB with native C# driver
namespace PestControl.Database;

public class CentipedeDbConnection : DbConnection
{
    private readonly string _connectionString;
    private bool _isOpen;

    public CentipedeDbConnection(string connectionString)
    {
        _connectionString = connectionString;
    }

    public override string ConnectionString
    {
        get => _connectionString;
        set => throw new NotSupportedException();
    }

    public override void Open()
    {
        // Custom connection logic for CentipedeDB
        CentipedeNativeClient.Connect(_connectionString);
        _isOpen = true;
    }

    // Implement all other DbConnection members...
}

// Full provider implementation required
public class CentipedeDbMethods : IDatabaseMethods
{
    public async Task<bool> CreateTableIfNotExistsAsync(
        IDbConnection db,
        DmTable table,
        IDbTransaction tx = null,
        CancellationToken cancellationToken = default)
    {
        // Generate CentipedeDB-specific CREATE TABLE syntax
        var sql = GenerateCentipedeCreateTableSql(table);

        // Execute using CentipedeDB's native command execution
        return await ExecuteCentipedeCommandAsync(db, sql, tx, cancellationToken);
    }

    // Implement all IDatabaseMethods interface methods...
    // This is the most work-intensive approach as every method needs
    // custom implementation for your database's specific syntax and capabilities
}

public class CentipedeDbMethodsFactory : DatabaseMethodsFactoryBase
{
    public override bool SupportsConnection(IDbConnection db)
        => db is CentipedeDbConnection;

    protected override IDatabaseMethods CreateMethodsCore()
        => new CentipedeDbMethods();
}

// Registration and usage
DatabaseMethodsProvider.RegisterFactory("CentipedeDb", new CentipedeDbMethodsFactory());

using var connection = new CentipedeDbConnection("Host=centipede-cluster;Database=analytics");
var customTable = new DmTable(null /* or schemaName */, "CustomTable", customColumns);
await connection.CreateTableIfNotExistsAsync(customTable);

Additional Custom Provider Ideas

Distributed Tracing Integration

csharp
public class TracingDbConnection : IDbConnection
{
    private readonly IDbConnection _innerConnection;
    private readonly ActivitySource _activitySource;

    public TracingDbConnection(IDbConnection innerConnection)
    {
        _innerConnection = innerConnection;
        _activitySource = new ActivitySource("MyApp.Database");
    }

    // Wrap all operations with OpenTelemetry activities for observability
}

Connection Pooling Wrapper

csharp
public class PooledDbConnection : IDbConnection
{
    private readonly IConnectionPool _pool;
    private IDbConnection _currentConnection;

    // Implement smart connection pooling with health checks
    // and automatic connection recycling
}

Audit Trail Provider

csharp
public class AuditingDbConnection : IDbConnection
{
    private readonly IDbConnection _innerConnection;
    private readonly IAuditLogger _auditLogger;

    // Log all DDL operations for compliance and tracking
    // Include user context, timestamps, and operation details
}

Registration Best Practices

  1. Register Early: Register custom providers during application startup, before any DapperMatic operations.
csharp
// In Program.cs or Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    // Register custom providers first
    DatabaseMethodsProvider.RegisterFactory("ProfiledDbConnection.SqlServer",
        new ProfiledSqlServerMethodsFactory());
    DatabaseMethodsProvider.RegisterFactory("ResilientDbConnection.PostgreSql",
        new ResilientPostgreSqlMethodsFactory());

    // Then configure other services
    services.AddScoped<IDbConnection>(provider =>
        new ProfiledDbConnection(baseConnection, MiniProfiler.Current));
}
  1. Use Descriptive Names: Choose factory names that clearly indicate both the wrapper and underlying provider.

  2. Test Thoroughly: Custom providers should be tested with the same rigor as built-in providers.

csharp
[Test]
public async Task CustomProvider_CreateTable_ShouldWork()
{
    using var connection = new CustomDbConnection(connectionString);
    var table = new DmTable("schema", "TestTable", columns);
    var result = await connection.CreateTableIfNotExistsAsync(table);

    Assert.IsTrue(result);
    Assert.IsTrue(await connection.DoesTableExistAsync("schema", "TestTable"));
}
  1. Handle Provider-Specific Features: Custom providers may have unique capabilities or limitations.
csharp
public class CustomMethodsFactory : DatabaseMethodsFactoryBase
{
    protected override IDatabaseMethods CreateMethodsCore()
    {
        var methods = new CustomMethods();

        // Configure provider-specific settings
        methods.SupportsTransactions = true;
        methods.SupportsSchemas = false;
        methods.MaxIdentifierLength = 128;

        return methods;
    }
}

When to Use Custom Providers

  • Wrapper Providers when you need to add cross-cutting concerns (profiling, logging, resilience) to existing database connections
  • Native Providers when working with databases not supported by DapperMatic out-of-the-box
  • Specialized Providers when you need custom connection behavior, pooling strategies, or protocol implementations

Custom providers ensure DapperMatic can work with virtually any IDbConnection implementation while maintaining the same consistent API across all your database interactions.

Connection String Examples

csharp
// Windows Authentication
"Server=localhost;Database=MyApp;Integrated Security=true;"

// SQL Authentication
"Server=localhost;Database=MyApp;User Id=user;Password=pass;"
csharp
// Standard connection
"Server=localhost;Database=myapp;Uid=user;Pwd=password;"

// With SSL
"Server=localhost;Database=myapp;Uid=user;Pwd=password;SslMode=Required;"
csharp
// Standard connection
"Host=localhost;Database=myapp;Username=user;Password=password;"

// With connection pooling
"Host=localhost;Database=myapp;Username=user;Password=password;Pooling=true;Maximum Pool Size=20;"
csharp
// File-based
"Data Source=myapp.db"

// In-memory (testing)
"Data Source=:memory:"

Data Type Mapping

DapperMatic automatically maps .NET types to appropriate database-specific types. The tables below show how each .NET type is represented across all supported database providers based on the comprehensive test suite in DatabaseMethodsTests.Types.cs.

Source of Truth

These mappings are verified by automated tests. For the most up-to-date and detailed type mappings, see tests/MJCZone.DapperMatic.Tests/DatabaseMethodsTests.Types.cs.

Integer Types (Default Mappings)

.NET TypeSQL ServerMySQLPostgreSQLSQLiteNotes
byteTINYINT(3)TINYINT(3)INT2TINYINTPostgreSQL: SMALLINT alias
sbyteTINYINT(3)TINYINT(3)INT2TINYINTPostgreSQL: SMALLINT alias
shortSMALLINT(5)SMALLINT(5)INT2SMALLINTPostgreSQL: SMALLINT alias
intINT(10)INT(10)INT4INTPostgreSQL: INTEGER alias
longBIGINT(19)BIGINT(19)INT8BIGINTPostgreSQL: BIGINT alias

Integer Type Notes

  • PostgreSQL Internal Names: INT2, INT4, INT8 are internal PostgreSQL type names (aliases for SMALLINT, INTEGER, BIGINT)
  • Display Width: MySQL 8.0.19+ deprecated display widths except for TINYINT(1) (boolean). Older versions may show different widths.
  • SQLite Type Affinity: SQLite maps all integer types to INTEGER storage class but preserves type names for compatibility

Floating Point Types

.NET TypeSQL ServerMySQLPostgreSQLSQLiteNotes
floatREAL(24)DOUBLE(22)FLOAT4REALMySQL uses DOUBLE; PostgreSQL: REAL alias
doubleFLOAT(53)FLOAT(12)FLOAT8DOUBLEPostgreSQL: DOUBLE PRECISION alias

Floating Point Notes

  • PostgreSQL Internal Names: FLOAT4, FLOAT8 are internal names for REAL and DOUBLE PRECISION
  • MySQL Behavior: Maps float to DOUBLE instead of FLOAT for better precision

Decimal Types

.NET TypeSQL ServerMySQLPostgreSQLSQLitePrecisionScaleNotes
decimalDECIMAL(16,4)DECIMAL(16,4)NUMERIC(16,4)NUMERIC(16,4)DefaultDefaultDefault precision/scale
decimalDECIMAL(12,8)DECIMAL(12,8)NUMERIC(12,8)NUMERIC(12,8)128Custom precision/scale
decimalDECIMAL(12)DECIMAL(12)NUMERIC(12)NUMERIC(12)120Precision only (scale=0)

Decimal Configuration

Default precision is 16, default scale is 4. Customize using DmColumn attribute:

csharp
[DmColumn("Price", precision: 18, scale: 2)]
public decimal Price { get; set; }

Boolean Type

.NET TypeSQL ServerMySQLPostgreSQLSQLiteNotes
boolBITTINYINT(1)BOOLBOOLEANMySQL: TINYINT(1) convention

Boolean Notes

  • MySQL: Uses TINYINT(1) as boolean convention (0=false, 1=true). BOOLEAN is an alias that becomes TINYINT(1).
  • PostgreSQL: BOOL is an alias for BOOLEAN

Character Type

.NET TypeSQL ServerMySQLPostgreSQLSQLiteUnicodeNotes
charCHAR(1)CHAR(1)BPCHAR(1)CHAR(1)NoDefault (non-unicode)
charNCHAR(1)CHAR(1)BPCHAR(1)NCHAR(1)YesUnicode

Character Type Notes

  • PostgreSQL: BPCHAR = "blank-padded char" (internal name for CHAR type)
  • SQLite: Uses CHAR(1)/NCHAR(1) for consistency and round-tripping, even though it stores as TEXT internally
  • Unicode: SQL Server and SQLite differentiate CHAR (ASCII) vs NCHAR (Unicode). MySQL and PostgreSQL ignore the unicode flag.

String Types (Variable Length)

.NET TypeSQL ServerMySQLPostgreSQLSQLiteUnicodeLengthNotes
stringNVARCHAR(255)VARCHAR(255)VARCHAR(255)VARCHAR(255)YesDefaultDefault: 255 characters
stringVARCHAR(255)VARCHAR(255)VARCHAR(255)VARCHAR(255)NoDefaultNon-unicode
stringNVARCHAR(234)VARCHAR(234)VARCHAR(234)NVARCHAR(234)Yes234Custom length
stringVARCHAR(234)VARCHAR(234)VARCHAR(234)VARCHAR(234)No234Custom length, non-unicode
stringNVARCHAR(MAX)TEXT(65535)TEXTNVARCHARYes-1 or int.MaxValueUnlimited length

String Types (Fixed Length)

.NET TypeSQL ServerMySQLPostgreSQLSQLiteUnicodeLengthIsFixedLengthNotes
stringCHAR(234)CHAR(234)BPCHAR(234)CHAR(234)No234YesFixed-length CHAR
stringNCHAR(234)CHAR(234)BPCHAR(234)NCHAR(234)Yes234YesFixed-length NCHAR

String Type Configuration

csharp
// Default (variable length, unicode, 255 chars)
public string Name { get; set; }

// Custom length
[DmColumn(length: 100)]
public string ShortName { get; set; }

// Unlimited length
[DmColumn(length: -1)]  // or int.MaxValue
public string LongText { get; set; }

// Fixed length (for codes, etc)
[DmColumn(length: 10, isFixedLength: true)]
public string Code { get; set; }

// Non-unicode (SQL Server only)
[DmColumn(isUnicode: false)]
public string AsciiOnly { get; set; }

GUID Type

.NET TypeSQL ServerMySQLPostgreSQLSQLiteNotes
GuidUNIQUEIDENTIFIERCHAR(36)UUIDVARCHAR(36)

Date & Time Types

.NET TypeSQL ServerMySQLPostgreSQLSQLiteNotes
DateTimeDATETIMEDATETIMETIMESTAMPDATETIME
DateTimeOffsetDATETIMEOFFSETTIMESTAMPTIMESTAMPTZDATETIMEMySQL: TIMESTAMP type
TimeSpanTIMETIMEINTERVALTIMEPostgreSQL: INTERVAL for durations
DateOnlyDATEDATEDATEDATE.NET 6+
TimeOnlyTIMETIMETIMETIME.NET 6+

Date & Time Notes

  • PostgreSQL: TIMESTAMPTZ = timestamp with time zone, INTERVAL for time spans/durations
  • MySQL: DateTime precision defaults can vary by version. Modern MySQL supports fractional seconds.
  • SQLite: Stores date/time as TEXT, INTEGER, or REAL. DapperMatic uses TEXT format for compatibility.

Binary Types

.NET TypeSQL ServerMySQLPostgreSQLSQLiteNotes
byte[]VARBINARY(255)VARBINARY(255)BYTEABLOBDefault: 255 bytes
Memory<byte>VARBINARY(255)VARBINARY(255)BYTEABLOB
ReadOnlyMemory<byte>VARBINARY(255)VARBINARY(255)BYTEABLOB
StreamVARBINARY(MAX)LONGBLOBBYTEABLOBUnlimited (large binary)
MemoryStreamVARBINARY(MAX)LONGBLOBBYTEABLOBUnlimited (large binary)

Binary Type Notes

  • Default Length: Binary types default to 255 bytes. Streams default to unlimited (MAX/LONGBLOB).
  • PostgreSQL: BYTEA has no length limit - it's variable-length by nature
  • MySQL: LONGBLOB can store up to 4GB
  • Custom Length: Use length parameter for custom sizes: [DmColumn(length: 1024)]

JSON & Complex Types

.NET TypeSQL ServerMySQLPostgreSQLSQLiteUnicodeNotes
System.Text.Json.JsonDocumentVARCHAR(MAX)JSONJSONBTEXTNoNon-unicode
System.Text.Json.JsonDocumentNVARCHAR(MAX)JSONJSONBTEXTYesUnicode (default)
System.Text.Json.JsonElementVARCHAR(MAX)JSONJSONBTEXTNoNon-unicode
System.Text.Json.JsonElementNVARCHAR(MAX)JSONJSONBTEXTYesUnicode (default)
System.Text.Json.Nodes.JsonArrayNVARCHAR(MAX)JSONJSONBTEXTYes
System.Text.Json.Nodes.JsonObjectNVARCHAR(MAX)JSONJSONBTEXTYes
System.Text.Json.Nodes.JsonValueNVARCHAR(MAX)JSONJSONBTEXTYes
objectVARCHAR(MAX)JSONJSONBTEXTNoNon-unicode
objectNVARCHAR(MAX)JSONJSONBTEXTYesUnicode

JSON & Complex Type Notes

  • PostgreSQL: JSONB = binary JSON (preferred over JSON for performance)
  • MySQL: Native JSON type (MySQL 5.7+). MariaDB 10.x: JSON is an alias for LONGTEXT with JSON validation
  • SQL Server: No native JSON type. Uses VARCHAR(MAX)/NVARCHAR(MAX) for JSON text and object storage

Enum Types

.NET TypeSQL ServerMySQLPostgreSQLSQLiteNotes
DayOfWeek (enum)INT(10)INT(10)INT4INTStored as underlying integer type
CustomEnum (byte)TINYINT(3)TINYINT(3)INT2TINYINTbyte-based enum
CustomEnum (short)SMALLINT(5)SMALLINT(5)INT2SMALLINTshort-based enum
CustomEnum (long)BIGINT(19)BIGINT(19)INT8BIGINTlong-based enum

Enum Type Notes

  • Storage: Enums are stored as their underlying integer type (byte, short, int, or long)
  • Default: C# enums default to int unless explicitly specified otherwise
  • Compatibility: This aligns with Dapper's default enum handling, making DML queries work seamlessly
  • DML Queries: When using Dapper's QueryAsync/ExecuteAsync with DapperMatic-created tables, enums work naturally without custom type handlers

Array Types

.NET TypeSQL ServerMySQLPostgreSQLSQLiteUnicodeLengthNotes
string[]VARCHAR(MAX)JSON_TEXTTEXTNo-1PostgreSQL: native array (internal notation)
int[]VARCHAR(MAX)JSON_INT4TEXTNo-1PostgreSQL: native array (internal notation)
long[]VARCHAR(MAX)JSON_INT8TEXTNo-1PostgreSQL: native array (internal notation)
Guid[]VARCHAR(MAX)JSON_UUIDTEXTNo-1PostgreSQL: native array (internal notation)
char[]VARCHAR(255)VARCHAR(255)VARCHAR(255)VARCHAR(255)NoDefaultTreated as string, not array
char[]NVARCHAR(MAX)TEXT(65535)TEXTNVARCHARYes-1Unlimited (treated as string)

Array Type Notes

  • PostgreSQL Native Arrays:
    • PostgreSQL has true native array support for primitive types
    • Internal notation uses underscore prefix: _TEXT, _INT4, _INT8, _UUID
    • Standard notation uses suffix: text[], integer[], bigint[], uuid[]
    • DapperMatic recognizes both notations when reading schema
  • SQL Server, MySQL, SQLite: No native array support. Arrays are serialized as JSON or TEXT.
  • char[] is special: treated as a string (character array), not a typed array
  • MariaDB 10.x: JSON is actually LONGTEXT with validation

Collection Types

All collection types are serialized as JSON:

.NET TypeSQL ServerMySQLPostgreSQLSQLiteNotes
List<string>VARCHAR(MAX)JSONJSONBTEXTJSON array
IList<string>VARCHAR(MAX)JSONJSONBTEXTJSON array
ICollection<string>VARCHAR(MAX)JSONJSONBTEXTJSON array
IEnumerable<string>VARCHAR(MAX)JSONJSONBTEXTJSON array
Dictionary<string, string>VARCHAR(MAX)JSONHSTORETEXTPostgreSQL: HSTORE for string-string maps
IDictionary<string, string>VARCHAR(MAX)JSONHSTORETEXTPostgreSQL: HSTORE for string-string maps

Collection Type Notes

  • All collections are serialized as JSON for storage
  • PostgreSQL HSTORE: Special type for Dictionary<string, string> - efficient key-value storage
  • PostgreSQL JSONB: Binary JSON format for other collections (faster indexing than JSON)
  • MariaDB 10.x: JSON is actually LONGTEXT with JSON validation
  • Generic types work the same: List<T>, Dictionary<K,V>, etc.

Auto-Increment Configuration

csharp
new DmColumn("Id", typeof(int))
{
    IsNullable = false,
    IsAutoIncrement = true // Creates IDENTITY(1,1)
}
csharp
new DmColumn("Id", typeof(int))
{
    IsNullable = false,
    IsAutoIncrement = true // Creates AUTO_INCREMENT
}
csharp
new DmColumn("Id", typeof(int))
{
    IsNullable = false,
    IsAutoIncrement = true // Creates SERIAL/IDENTITY
}
csharp
new DmColumn("Id", typeof(int))
{
    IsNullable = false,
    IsAutoIncrement = true // Uses INTEGER PRIMARY KEY
}

General Type Mapping Notes

Key Takeaways

  • Defaults are sensible: String length defaults to 255, binary to 255, decimal to (16,4)
  • Unlimited values: Use length: -1 or int.MaxValue for VARCHAR(MAX), TEXT, LONGBLOB, etc.
  • PostgreSQL specifics: Uses internal notation (INT2, INT4, FLOAT8, _TEXT, etc.) when reading schema
  • MariaDB 10.x JSON: JSON type is actually LONGTEXT with validation (not binary JSON like MySQL 5.7+)
  • Source of truth: See DatabaseMethodsTests.Types.cs for complete test coverage

Customizing Type Mappings:

csharp
// Custom length
[DmColumn(length: 500)]
public string LongName { get; set; }

// Custom precision and scale
[DmColumn(precision: 18, scale: 6)]
public decimal HighPrecisionValue { get; set; }

// Fixed-length strings
[DmColumn(length: 10, isFixedLength: true)]
public string CountryCode { get; set; }

// Explicit provider-specific types (multi-database support)
[DmColumn(providerDataType: "{sqlserver:money,mysql:decimal(19,4),postgresql:money,sqlite:real}")]
public decimal Price { get; set; }

// Simple provider type (single database)
[DmColumn(providerDataType: "money")]
public decimal Amount { get; set; }

Reverse Type Mapping (Database → .NET)

This section shows how database-specific types are mapped back to .NET types when reading schema information. This is critical for understanding how DapperMatic reverse-engineers database columns into .NET types with full fidelity including length, precision, and scale.

Round-Trip Fidelity

These mappings ensure that when you:

  1. Create a column from a .NET type → Database type
  2. Read the column back → Database type → .NET type

You get the same .NET type with the same metadata (length/precision/scale).

Priority-Based Type Selection Strategy

When reverse-engineering database schemas (DDL operations), DapperMatic uses a priority-based type selection strategy for advanced types like spatial data, hierarchical data, and specialized PostgreSQL types. This ensures the best possible .NET type is selected based on available assemblies:

Priority Order:

  1. Native provider types (if assembly available) - e.g., MySql.Data.Types
  2. Cross-platform types (if assembly available) - e.g., NetTopologySuite.Geometries.*
  3. Fallback to primitives - string (text serialization) or byte[] (binary serialization)

Optional Assembly Dependencies:

DapperMatic automatically detects these optional assemblies at runtime:

  • NetTopologySuite (NetTopologySuite NuGet package) - Cross-database spatial types
  • MySql.Data (MySql.Data NuGet package) - MySQL spatial types (alternative to MySqlConnector)

Examples:

Database TypeAvailable AssembliesSelected .NET TypeFallback (if not available)
MySQL pointNetTopologySuiteNetTopologySuite.Geometries.Pointstring (WKT format)
MySQL geometryMySql.Data (no NTS)MySql.Data.Types.MySqlGeometrystring (WKT format)
PostgreSQL geometry (PostGIS)NetTopologySuiteNetTopologySuite.Geometries.Geometrystring (WKT format)
PostgreSQL ltree-string-
PostgreSQL regclass (OID)-uint-

Assembly Detection

DapperMatic uses the AssemblyDetector class to lazily detect optional assemblies at runtime. This means:

  • No hard dependencies - You only need to install the packages you actually use
  • Zero configuration - Detection happens automatically
  • Performance optimized - Results are cached after first detection

Recommendation for Multi-Database Applications

For applications that work with multiple database providers and need spatial types, we recommend installing NetTopologySuite as it provides consistent spatial type support across SQL Server, MySQL, and PostgreSQL.

bash
dotnet add package NetTopologySuite

This gives you consistent Geometry, Point, LineString, etc. types regardless of which database you're using.

SQL Server Types → .NET Types

SQL Server Type.NET TypeLengthPrecisionScaleUnicodeNotes
bigintlong64-bit integer
binarybyte[]1Fixed-length binary
bitboolBoolean (0 or 1)
charstring1YesFixed-length char
dateDateOnlyDate only (no time)
datetimeDateTimeDate and time
datetime2DateTimeDate and time (higher precision)
datetimeoffsetDateTimeOffsetDate, time, and timezone offset
decimaldecimal182Fixed-point decimal
floatdouble64-bit floating point
imagebyte[]Legacy binary type (deprecated)
intint32-bit integer
moneydecimalCurrency type
ncharstring1YesFixed-length Unicode char
ntextstringYesLegacy Unicode text (deprecated)
numericdecimal182Fixed-point numeric (same as decimal)
nvarcharstring255YesVariable-length Unicode string
nvarchar(max)string-1YesUnlimited Unicode string
realfloat32-bit floating point
rowversionDateTimeRow version timestamp
smalldatetimeDateTimeDate and time (lower precision)
smallintshort16-bit integer
smallmoneydecimalSmall currency type
sql_variantobjectVariable type container
textstringYesLegacy text type (deprecated)
timeTimeOnlyTime of day only
timestampDateTimeAlias for rowversion
tinyintbyte8-bit unsigned integer
uniqueidentifierGuidGUID/UUID
varbinarybyte[]1Variable-length binary
varbinary(max)byte[]-1Unlimited binary data
varcharstring255YesVariable-length string
varchar(max)string-1YesUnlimited string
xmlXDocumentXML data type

MySQL Types → .NET Types

MySQL Type.NET TypeLengthPrecisionScaleUnicodeNotes
bigintlong64-bit integer
binarybyte[]1Fixed-length binary
bitboolBoolean/bit field
blobbyte[]YesBinary large object
booleanboolAlias for TINYINT(1)
charstring1YesFixed-length char
dateDateOnlyDate only
datetimeDateTimeDate and time
decimaldecimal102Fixed-point decimal
doubledouble64-bit floating point
enumstringYesEnumeration type
floatfloat32-bit floating point
geometryGeometry or MySqlGeometry or stringSpatial geometry type (NetTopologySuite or MySql.Data)
geometrycollectionGeometryCollection or MySqlGeometry or stringSpatial collection (NetTopologySuite or MySql.Data)
intint32-bit integer
jsonMySQL: JsonDocument
MariaDB: string
YesJSON type (MySQL 5.7+) / TEXT with validation (MariaDB 10.x)
linestringLineString or MySqlGeometry or stringSpatial linestring (NetTopologySuite or MySql.Data)
longblobbyte[]YesLarge binary object (up to 4GB)
longtextstringYesLarge text (up to 4GB)
mediumblobbyte[]YesMedium binary object
mediumintint24-bit integer
mediumtextstringYesMedium text
multilinestringMultiLineString or MySqlGeometry or stringSpatial multi-linestring (NetTopologySuite or MySql.Data)
multipointMultiPoint or MySqlGeometry or stringSpatial multi-point (NetTopologySuite or MySql.Data)
multipolygonMultiPolygon or MySqlGeometry or stringSpatial multi-polygon (NetTopologySuite or MySql.Data)
pointPoint or MySqlGeometry or stringSpatial point (NetTopologySuite or MySql.Data)
polygonPolygon or MySqlGeometry or stringSpatial polygon (NetTopologySuite or MySql.Data)
setstringYesSET enumeration type
smallintshort16-bit integer
textstringYesText
timeTimeOnlyTime of day
timestampDateTimeOffsetTimestamp with timezone
tinyblobbyte[]YesTiny binary object
tinyintsbyte8-bit signed integer
tinytextstringYesTiny text
varbinarybyte[]255YesVariable-length binary
varcharstring255YesVariable-length string
yearintYear (2 or 4 digit)

MySQL vs MariaDB

  • MySQL 5.7+: JSON type is true binary JSON (JsonDocument)
  • MariaDB 10.x: JSON is an alias for LONGTEXT with JSON validation constraints (maps to string)

PostgreSQL Types → .NET Types

PostgreSQL has the richest type system with native arrays, ranges, network types, and more.

Core Data Types

PostgreSQL Type.NET TypeLengthPrecisionScaleUnicodeNotes
bigintlong64-bit integer
bigseriallongAuto-incrementing bigint
bitstringYesBit string
bit varyingstringYesVariable-length bit string
booleanboolBoolean
boxNpgsqlBoxRectangular box
byteabyte[]Binary data
characterstring1YesFixed-length char
character varyingstring255YesVariable-length string (VARCHAR)
cidrNpgsqlCidrCIDR network address
circleNpgsqlCircleCircle
dateDateOnlyDate only
daterangeNpgsqlRange<DateOnly>Range of dates
double precisiondouble64-bit floating point
geographyGeometry or stringPostGIS geography type (NetTopologySuite)
geometryGeometry or stringPostGIS geometry type (NetTopologySuite)
hstoreDictionary<string,string>Key-value store
inetIPAddressIP address
int4rangeNpgsqlRange<int>Range of integers
int8rangeNpgsqlRange<long>Range of bigints
integerint32-bit integer
intervalTimeSpanTime interval/duration
jsonJsonDocumentJSON text
jsonbJsonDocumentBinary JSON (preferred)
lineNpgsqlLineInfinite line
lsegNpgsqlLSegLine segment
ltreestringLabel tree (requires extension) - stored as hierarchical text path
macaddrPhysicalAddressMAC address
macaddr8PhysicalAddressMAC address (EUI-64)
moneydecimalCurrency
numericdecimal182Fixed-point numeric
numrangeNpgsqlRange<decimal>Range of numeric values (exact precision)
oiduintObject identifier
pathNpgsqlPathGeometric path
pointNpgsqlPointGeometric point
polygonNpgsqlPolygonGeometric polygon
realfloat32-bit floating point
regclassuintRegistered class (OID reference)
regconfiguintText search configuration (OID reference)
regdictionaryuintText search dictionary (OID reference)
regoperuintRegistered operator (OID reference)
regoperatoruintRegistered operator with signature (OID reference)
regprocuintRegistered procedure (OID reference)
regprocedureuintRegistered procedure with signature (OID reference)
regtypeuintRegistered type (OID reference)
serialintAuto-incrementing integer
smallintshort16-bit integer
smallserialshortAuto-incrementing smallint
textstringYesVariable-length text
timeTimeOnlyTime of day
time with time zoneTimeOnlyTime with timezone
timestampDateTimeTimestamp without timezone
timestamp with time zoneDateTimeOffsetTimestamp with timezone
tsqueryNpgsqlTsQueryText search query
tsrangeNpgsqlRange<DateTime>Range of timestamps
tstzrangeNpgsqlRange<DateTimeOffset>Range of timestamps with timezone
tsvectorNpgsqlTsVectorText search vector
uuidGuidUUID
xmlXDocumentXML data

PostgreSQL Array Types

PostgreSQL supports native typed arrays. Both notations are recognized:

PostgreSQL Type (Standard)PostgreSQL Type (Internal).NET TypeNotes
bigint[]_int8long[]Array of 64-bit integers
boolean[]_boolbool[]Array of booleans
bytea[]_byteabyte[][]Array of byte arrays
character[]_bpcharchar[]Array of characters
character varying[]_varcharstring[]Array of strings
date[]_dateDateOnly[]Array of dates
double precision[]_float8double[]Array of 64-bit floats
integer[]_int4int[]Array of 32-bit integers
interval[]_intervalTimeSpan[]Array of time intervals
json[]_jsonJsonDocument[]Array of JSON documents
jsonb[]_jsonbJsonDocument[]Array of binary JSON
numeric[]_numericdecimal[]Array of decimals
real[]_float4float[]Array of 32-bit floats
smallint[]_int2short[]Array of 16-bit integers
text[]_textstring[]Array of text strings
time[]_timeTimeOnly[]Array of times
time with time zone[]_timetzTimeOnly[]Array of times with timezone
timestamp[]_timestampDateTime[]Array of timestamps
timestamp with time zone[]_timestamptzDateTimeOffset[]Array of timestamps with timezone
uuid[]_uuidGuid[]Array of UUIDs

PostgreSQL Array Notation

  • Standard notation: text[], integer[], etc. (used in CREATE TABLE)
  • Internal notation: _text, _int4, etc. (returned by system catalogs)
  • DapperMatic recognizes both notations when reading schema

SQLite Types → .NET Types

SQLite has only 5 storage classes but accepts many type names for compatibility.

SQLite Type.NET TypeLengthPrecisionScaleNotes
blobbyte[]Binary data (BLOB storage class)
booleanboolStored as INTEGER 0/1
charstring1Fixed-length char (TEXT storage class)
dateDateOnlyDate (TEXT/INTEGER/REAL storage)
datetimeDateTimeDate and time (TEXT/INTEGER/REAL storage)
decimaldecimal182Fixed-point decimal (TEXT/REAL storage)
integerintInteger (INTEGER storage class)
numericdecimalNumeric (any storage class with numeric affinity)
realdouble64-bit float (REAL storage class - 8 bytes)
textstringText (TEXT storage class)
timeTimeOnlyTime (TEXT/INTEGER/REAL storage)
timestampDateTimeTimestamp (TEXT/INTEGER/REAL storage)
varcharstring255Variable-length string (TEXT storage class)

SQLite Type Affinity

  • Storage Classes: NULL, INTEGER, REAL, TEXT, BLOB (only 5 fundamental types)
  • Type Affinity: SQLite determines storage class based on declared type name
  • Flexibility: Accepts many type names (varchar, decimal, datetime, etc.) for compatibility
  • Precision/Scale: SQLite accepts but doesn't enforce precision/scale - DapperMatic preserves these in schema for round-tripping
  • REAL is 8-byte: SQLite's REAL type stores 8-byte IEEE floating point (maps to .NET double, not float)

Precision and Scale Defaults

When database types support precision and scale, these are the defaults used:

ProviderTypeDefault PrecisionDefault ScaleMax PrecisionMax Scale
SQL Serverdecimal/numeric1823838
MySQLdecimal1026530
PostgreSQLnumeric/decimal18210001000
SQLitedecimal/numeric18210001000

Precision Differences

Note that MySQL defaults to precision=10 while other providers default to precision=18. This matches each database's actual defaults but means a decimal property will have different precision across databases unless explicitly specified.

Getting Help

If you encounter provider-specific issues:

  1. Check the troubleshooting guide
  2. Review the provider's documentation
  3. File an issue on GitHub