Introducing RepoDb.Connector.MariaDb - RepoDB’s de-facto connector for MariaDB in .NET

9 minute read

Published:

We just announced RepoDB Connectors, a new home for dedicated, provider-specific ADO.NET data providers built for RepoDB — read the announcement at Introducing RepoDB Connectors — Starting with MariaDB if you haven’t yet. This post goes one level deeper into the first connector that shipped out of it, RepoDb.Connector.MariaDb: why it exists, how it’s laid out internally, the objects it exposes, and enough code to get productive with it right away.

Status: Early development. The API and implementation are subject to change. Disclaimer: This is an independent, unofficial .NET provider for MariaDB. It is a thin ADO.NET wrapper and type-mapping layer built on top of MySql.Data and is not affiliated with or endorsed by MariaDB plc or the MariaDB Foundation.

Why does MariaDb need its own connector?

MySQL and MariaDb are close relatives, but they aren’t the same server, and RepoDB already ships dedicated extensions for both — RepoDb.MySql, RepoDb.MySqlConnector, and now RepoDb.MariaDb. Once you have three extensions in play, you need a way to introduce MariaDb-specific ADO.NET objects — connections, commands, parameters, types — without those objects colliding with the ones already registered for MySQL.

RepoDb.Connector.MariaDb is that answer: every public type is prefixed with MariaDb, so MariaDbConnection can live in the same process as MySqlConnection without ambiguity, while both ultimately still talk to the server through Oracle’s MySql.Data driver under the hood. It’s the same idea RepoDB already applies to SQL Server, PostgreSQL, and Oracle — a dedicated, standardized System.Data.Common surface per provider — now extended to MariaDb, and it’s what powers RepoDb.MariaDb and RepoDb.MariaDb.BulkOperations internally.

Architecture

RepoDb.Connector.MariaDb is more than a set of wrapper classes sitting in front of MySql.Data. Conceptually, the public ADO.NET API sits on top of the MariaDB communication and protocol infrastructure — authentication, TLS negotiation, prepared statement handling, parameter encoding, result set parsing, type encoding/decoding, and cancellation — which in turn talks the MariaDB wire protocol over TCP to the server:

Application / ORM
       │
       ▼
MariaDbConnection
       │
       ▼
MariaDbCommand
       │
       ▼
MariaDB Session
       │
       ├── Authentication
       ├── TLS
       ├── Prepared Statements
       ├── Parameter Encoding
       ├── Result Set Parsing
       ├── Type Encoding/Decoding
       └── Cancellation
               │
               ▼
        MariaDB Protocol
               │
               ▼
              TCP
               │
               ▼
        MariaDB Server

Because everything is expressed against System.Data.Common base classes, the connector composes naturally with anything that already speaks ADO.NET — RepoDB, Dapper, hand-rolled data access layers, or raw DbConnection/DbCommand code:

RepoDB
Dapper
Custom Data Access Layers
ADO.NET Applications
Other DbConnection-based Libraries
          │
          ▼
   RepoDb.Connector.MariaDb
          │
          ▼
      MariaDB Server

The connector itself stays independent of any ORM — RepoDB is simply the first (and primary) consumer.

The objects

The core of the connector is a standard ADO.NET provider implementation — one MariaDb-prefixed type per System.Data.Common abstraction:

RepoDb.Connector.MariaDbADO.NET Base ClassPurpose
MariaDbConnectionDbConnectionEstablishes and manages MariaDB connections
MariaDbCommandDbCommandExecutes SQL commands
MariaDbDataReaderDbDataReaderReads query results
MariaDbParameterDbParameterRepresents command parameters
MariaDbParameterCollectionDbParameterCollectionManages command parameters
MariaDbTransactionDbTransactionManages database transactions
MariaDbExceptionDbExceptionRepresents MariaDB errors
MariaDbConnectionStringBuilderDbConnectionStringBuilderBuilds and parses connection strings
MariaDbProviderFactoryDbProviderFactoryCreates provider-specific ADO.NET objects

Bulk-loading lives in its own namespace, RepoDb.Connector.MariaDb.Bulk, built on top of LOAD DATA LOCAL INFILE via MySql.Data’s MySqlBulkLoader:

RepoDb.Connector.MariaDb.BulkPurpose
MariaDbBulkCopyEfficiently bulk-loads a DbDataReader/IDataReader, DataTable, or DataRow[] into a table
MariaDbBulkColumnMappingDefines the mapping between a source column and a destination column
MariaDbBulkCopyColumnMappingCollectionThe collection of MariaDbBulkColumnMapping objects exposed by MariaDbBulkCopy.ColumnMappings
MariaDbBulkLoaderA strongly typed wrapper around LOAD DATA LOCAL INFILE, for loading from a file or stream
MariaDbBulkLoaderConflictOptionControls behavior when a key conflict arises during a load
MariaDbBulkLoaderPriorityControls the priority (None, Low, Concurrent) of a bulk load operation

Basic usage

At its simplest, RepoDb.Connector.MariaDb gives you the ordinary ADO.NET programming model, just with MariaDb-prefixed types:

using RepoDb.Connector.MariaDb;

var connectionString =
    "Server=localhost;" +
    "Port=3306;" +
    "Database=TestDb;" +
    "User ID=root;" +
    "Password=password;";

await using var connection =
    new MariaDbConnection(connectionString);

await connection.OpenAsync();

await using var command = connection.CreateCommand();

command.CommandText = """
    SELECT Id, Name, Email
    FROM Customer
    WHERE Id = @Id;
    """;

command.Parameters.AddWithValue("@Id", 100);

await using var reader = await command.ExecuteReaderAsync();

while (await reader.ReadAsync())
{
    var id = reader.GetInt32(0);
    var name = reader.GetString(1);
    var email = reader.GetString(2);

    Console.WriteLine($"{id}: {name} ({email})");
}

MariaDbConnection

MariaDbConnection extends DbConnection and owns connection establishment, state, session management, command/transaction creation, and connection string handling — synchronously and asynchronously:

await using var connection =
    new MariaDbConnection(connectionString);

await connection.OpenAsync();

Console.WriteLine(connection.ServerVersion);
Console.WriteLine(connection.Database);
Console.WriteLine(connection.State);

MariaDbCommand

MariaDbCommand extends DbCommand and represents a SQL statement executed against MariaDB — parameterized SQL, prepared statements, command timeout, cancellation, and multiple result sets are all part of the target surface:

await using var command = new MariaDbCommand(
    "SELECT * FROM Customer WHERE Id = @Id",
    connection);

command.Parameters.AddWithValue("@Id", 100);

await using var reader =
    await command.ExecuteReaderAsync();

MariaDbParameter and MariaDbType

MariaDbParameter extends DbParameter and represents a single parameter on a MariaDbCommand:

var parameter = new MariaDbParameter
{
    ParameterName = "@Id",
    Value = 100
};

command.Parameters.Add(parameter);

Alongside the standard DbType, the connector introduces a MariaDbType enumeration for MariaDB-specific data type mapping — covering numeric types (TinyInt through BigInt, Decimal, Float, Double, Bit), text types (Char through LongText, Enum, Set), binary types (Binary through LongBlob), temporal types (Date, Time, DateTime, Timestamp, Year), Json, and the full spatial family (Geometry, Point, LineString, Polygon, and their Multi*/GeometryCollection counterparts):

var parameter = new MariaDbParameter
{
    ParameterName = "@Id",
    MariaDbType = MariaDbType.Int,
    Value = 100
};

The mapping chain runs both directions — MariaDbTypeDbType ↔ .NET CLR type ↔ MariaDB server type — and MariaDbTypeConverter currently covers the leg between MariaDbType and MySql.Data.MySqlClient.MySqlDbType:

var mariaDbType = MariaDbTypeConverter.ToMariaDbType(MySqlDbType.VarChar);
var mySqlDbType = MariaDbTypeConverter.ToMySqlDbType(MariaDbType.BigInt);

Transactions

MariaDbTransaction extends DbTransaction and provides the usual ADO.NET transaction semantics — begin, commit, rollback, sync and async:

await using var transaction =
    await connection.BeginTransactionAsync();

try
{
    await using var command = connection.CreateCommand();

    command.Transaction = transaction;

    command.CommandText =
        "UPDATE Customer SET Name = @Name WHERE Id = @Id";

    command.Parameters.AddWithValue("@Name", "John Doe");
    command.Parameters.AddWithValue("@Id", 100);

    await command.ExecuteNonQueryAsync();

    await transaction.CommitAsync();
}
catch
{
    await transaction.RollbackAsync();
    throw;
}

Connection string builder

MariaDbConnectionStringBuilder extends DbConnectionStringBuilder, giving you a strongly typed way to assemble connection strings instead of hand-concatenating them:

var builder = new MariaDbConnectionStringBuilder
{
    Server = "localhost",
    Port = 3306,
    Database = "TestDb",
    UserId = "root",
    Password = "password"
};

await using var connection =
    new MariaDbConnection(builder.ConnectionString);

await connection.OpenAsync();

Provider factory

MariaDbProviderFactory extends DbProviderFactory, letting provider-independent ADO.NET code create RepoDb.Connector.MariaDb objects without a hard compile-time reference to the concrete types:

var factory = MariaDbProviderFactory.Instance;

using var connection = factory.CreateConnection();

connection.ConnectionString = connectionString;
connection.Open();

Bulk operations

Row-by-row INSERT statements cost a round-trip each. When you’re moving thousands or millions of rows — an import, a migration, a catch-up sync — that overhead adds up. RepoDb.Connector.MariaDb.Bulk addresses that by wrapping MariaDB’s LOAD DATA LOCAL INFILE mechanism, exposed through Oracle’s MySql.Data-provided MySqlBulkLoader.

MariaDbBulkCopy

MariaDbBulkCopy writes its source rows to a temporary file and loads them through MariaDbBulkLoader. It accepts an IDataReader, a DbDataReader, a DataTable (optionally filtered by DataRowState), or a DataRow[]:

await using var connection =
    new MariaDbConnection(connectionString);

await connection.OpenAsync();

using var bulkCopy = new MariaDbBulkCopy(connection)
{
    DestinationTableName = "Customer",
    BatchSize = 10_000
};

bulkCopy.ColumnMappings.Add("Id", "Id");
bulkCopy.ColumnMappings.Add("Name", "Name");
bulkCopy.ColumnMappings.Add("Email", "Email");

await bulkCopy.WriteToServerAsync(customersDataTable);

Console.WriteLine(bulkCopy.RowsCopied);

MariaDbBulkLoader

If you already have a file or stream on disk, MariaDbBulkLoader can be used directly, without going through MariaDbBulkCopy:

var bulkLoader = new MariaDbBulkLoader(connection)
{
    TableName = "Customer",
    FileName = "customers.csv",
    FieldTerminator = ",",
    LineTerminator = "\n",
    Local = true
};

bulkLoader.Columns.Add("Id");
bulkLoader.Columns.Add("Name");
bulkLoader.Columns.Add("Email");

var rowsLoaded = await bulkLoader.LoadAsync();

MariaDbBulkLoaderConflictOption controls how a load reacts to key conflicts, and MariaDbBulkLoaderPriority (None, Low, Concurrent) controls how the load is scheduled relative to other server activity.

Where this fits with RepoDB

If you’re already using RepoDb.MariaDb for fluent CRUD or RepoDb.MariaDb.BulkOperations for BulkInsert/BulkUpdate/BulkMerge/BulkDelete, you’re already exercising this connector — every MariaDbConnection those extensions create underneath is the exact type covered in this post. You don’t need to change anything to benefit from it; it’s the layer that makes RepoDB’s provider-agnostic API work correctly against MariaDB specifically, without colliding with the MySQL-flavored objects RepoDB already registers.

You can also use it directly, with no ORM at all, or hand it to any other library that only expects a DbConnection — Dapper included.

What’s next

The initial development effort prioritizes the core connection, command, parameter, transaction, and data-reader infrastructure. Beyond that, the roadmap includes connection pooling, richer prepared-statement support, TLS/SSL, additional authentication mechanisms, cancellation, multiple result sets, advanced server metadata, MariaDbDataAdapter, MariaDbCommandBuilder, and eventually a native (non LOAD DATA-based) bulk execution protocol.

As a preview, expect the API surface to keep moving. If you run into a rough edge — or want to help build out the MariaDB wire protocol implementation, authentication, or type mappings — contributions are very welcome.

Please support us

Give a ⭐ to our Github repository | Follow me at X / Twitter | Connect with us at our official Teams Channel
Apache License 2.0 — Copyright © 2026 by Michael Camara Pendon / Create a Request