Skip to content

Latest commit

 

History

256 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Doka.EntityFrameworkCore.MySql

CI NuGet MySQL / MariaDB NuGet NetTopologySuite NuGet Caching License: MIT OpenSSF Scorecard OpenSSF Best Practices

Doka.EntityFrameworkCore.MySql is an Entity Framework Core 10 provider for MySQL and MariaDB, built on the asynchronous MySqlConnector ADO.NET driver. It provides one EF Core model across both database families while keeping engine differences explicit, testable, and observable.

Use it when an application needs current .NET and EF Core support, a small public API, portable MySQL/MariaDB behavior, and qualification against every advertised LTS line.

Packages

Package Purpose
Doka.EntityFrameworkCore.MySql Core EF Core provider, migrations, scaffolding, type mappings, and query translation
Doka.EntityFrameworkCore.MySql.NetTopologySuite Optional NetTopologySuite mappings, spatial indexes, scaffolding, and spatial query translation
Doka.Caching.MySql Standalone .NET 10 IDistributedCache and IBufferDistributedCache implementation; available from 10.1.0

The cache package, connection-string detection, and scalar Like<T> are available from 10.1.0. They are not present in the 10.0.0 packages.

Requirements

  • An application targeting .NET 10 or later
  • EF Core 10.0.x for the provider and spatial extension; the supported package range is >= 10.0.8 and < 10.1.0
  • MySqlConnector 2.x; the supported package range is >= 2.5.0 and < 3.0.0
  • A supported MySQL or MariaDB server from the matrix below

Building the repository itself requires the exact .NET SDK declared in global.json. Docker is required only for live integration, example, benchmark, and release-qualification runs.

Install

The following .NET 10 command installs the latest stable provider package and writes its resolved version to the project file:

dotnet package add Doka.EntityFrameworkCore.MySql

Add the spatial extension only when the model uses NetTopologySuite types:

dotnet package add Doka.EntityFrameworkCore.MySql.NetTopologySuite

Install the standalone distributed cache only when the application needs a MySQL or MariaDB-backed IDistributedCache:

dotnet package add Doka.Caching.MySql

For reproducible installs, add --version followed by the exact version from the GitHub release or NuGet.org package page.

Current Stable Release

10.4.0 lets custom migration-operation handlers explicitly consume validation-only and control-only operations without generating synthetic SQL. Diagnostics, activities, metrics, and the bounded outcome code remain intact. It also prevents MariaDB JSON aliases from recreating their engine-owned JSON_VALID constraint as a duplicate user CHECK during reverse engineering. Char36 and Binary16 remain fully supported GUID storage formats. Pin the current stable version explicitly when validating an affected application:

dotnet package add Doka.EntityFrameworkCore.MySql --version 10.4.0

See Migration Operation Handlers for commandless outcomes and the public metadata projection, and Migrating from Pomelo for the GUID mapping and representation-migration contracts.

Quick Start

Configure the provider with the database family and server release line, then use the normal EF Core APIs:

using Doka.EntityFrameworkCore.MySql;
using Microsoft.EntityFrameworkCore;

var connectionString =
    "Server=localhost;Database=my_app;User ID=app;Password=secret;";

var serverVersion = MySqlServerVersion.MySql(new Version(8, 4, 0));

var options = new DbContextOptionsBuilder<AppDbContext>()
    .UseMySql(connectionString, serverVersion)
    .Options;

await using var context = new AppDbContext(options);

await context.Database.EnsureCreatedAsync();
context.Products.Add(new Product { Name = "Widget", Price = 9.99m });
await context.SaveChangesAsync();

var products = await context.Products
    .AsNoTracking()
    .OrderBy(product => product.Name)
    .ToListAsync();

EnsureCreatedAsync() keeps this first-use example small. Applications whose schema evolves should use EF Core migrations instead.

MariaDB uses the same provider surface with a different version factory:

var serverVersion = MySqlServerVersion.MariaDb(new Version(11, 8, 0));

When the exact server version is not known during configuration, detect it from an open connection:

using Doka.EntityFrameworkCore.MySql;
using MySqlConnector;

var connectionString =
    "Server=localhost;Database=my_app;User ID=app;Password=secret;";

await using var connection = new MySqlConnection(connectionString);
await connection.OpenAsync();

var serverVersion = MySqlServerVersion.AutoDetect(connection);

The new connection-string overload manages the temporary connection itself:

var serverVersion = MySqlServerVersion.AutoDetect(connectionString);

It opens synchronously once and disposes the connection on success or failure. Reuse the descriptor for an unchanged server target instead of detecting it for every context. Both detection paths use SupportedOnly by default; see Provider Configuration.

Dependency Injection

Register a context with a connection string:

services.AddDbContext<AppDbContext>(options =>
    options.UseMySql(
        connectionString,
        MySqlServerVersion.MySql(new Version(8, 4, 0))));

For centralized pooling and connector logging, register a MySqlDataSource instead:

var dataSourceConnectionString = new MySqlConnectionStringBuilder(connectionString)
{
    GuidFormat = MySqlConnector.MySqlGuidFormat.Binary16,
}.ConnectionString;

var dataSource = new MySqlDataSourceBuilder(dataSourceConnectionString)
    .UseLoggerFactory(loggerFactory)
    .Build();

services.AddDbContext<AppDbContext>(options =>
    options.UseMySql(
        dataSource,
        MySqlServerVersion.MySql(new Version(8, 4, 0))));

The provider also accepts an existing DbConnection. See Host Integration for connection ownership, pooling, retry, health-check, and telemetry guidance.

Provider-owned connection strings are normalized to Doka's Binary16 connector transport. Caller-owned connections and data sources must already specify GuidFormat=Binary16; Doka validates them without mutation. Every connection path rejects UseAffectedRows=true because EF optimistic concurrency requires matched-row semantics.

Supported Engines

Engine Supported line JSON storage Sequences RETURNING Temporal tables
MySQL 8.4 LTS native emulated unavailable in the engine provider emulation
MySQL 9.7 LTS native emulated unavailable in the engine provider emulation
MariaDB 10.11 LTS validated alias native native native
MariaDB 11.4 LTS validated alias native native native
MariaDB 11.8 LTS validated alias native native native
MariaDB 12.3 LTS validated alias native native native

All six lines provide native CTE support. The exact qualified patch pins, lifecycle sources, live-test ownership, and unsupported-version policy are in Supported Databases.

Runtime capability diagnostics classify provider behavior as Native, Emulated, or UnsupportedByEngine. Unsupported server releases are rejected by default. MySqlServerVersionCompatibilityMode.AllowUnsupported is an explicit escape hatch without a support guarantee and emits MySqlEventId.UnsupportedServerVersion.

Feature Highlights

  • Engine-aware migrations and scaffolding: advisory-lock protection, idempotent scripts, rename and sequence handling, generated and invisible columns, spatial indexes, JSON aliases, and custom migration-operation handlers.
  • Portable temporal modeling: native MariaDB system, application, and bitemporal tables plus provider-owned MySQL history-table emulation behind one model and query API.
  • MySQL-family query translation: JSON functions, regular expressions, full-text search, scalar Like<T>, CTE composition, bulk update/delete, and engine-specific SQL selected from declared capabilities.
  • Provider-owned type mappings: JSON DOM types, Binary16 and Char36 GUIDs, temporal CLR types, generated defaults, complex types, and optional NetTopologySuite geometries.
  • Production behavior: transient-failure retries, savepoints, connection pooling, structured diagnostics, trimming analysis, compiled models, and precompiled query coverage.
  • Standalone distributed caching: standard .NET cache contracts, database-UTC expiration, buffer-based reads, and bounded expired-row cleanup without an EF Core dependency.

The documentation index owns the complete behavioral contracts and limitations. The sections below show only the main entry points.

Retry transient failures

options.UseMySql(connectionString, serverVersion, mysql =>
    mysql.EnableRetryOnFailure(maxRetryCount: 5));

Require server-side user variables

Libraries or applications that use session-local @name variables can require the connector capability through provider configuration:

options.UseMySql(connectionString, serverVersion, mysql =>
    mysql.RequireUserVariables());

Doka adds AllowUserVariables=true when an owned connection string omits it. Borrowed connections and data sources must configure it explicitly because Doka never rebuilds caller-owned objects. See Provider Configuration for runtime replacement and ownership rules.

Choose GUID storage

options.UseMySql(connectionString, serverVersion, mysql =>
    mysql.DefaultGuidFormat(
        Doka.EntityFrameworkCore.MySql.MySqlGuidFormat.Char36));

modelBuilder.Entity<OrderWithGuid>()
    .Property(order => order.Id)
    .HasMySqlGuidFormat(
        Doka.EntityFrameworkCore.MySql.MySqlGuidFormat.Binary16);

Configure temporal tables

modelBuilder.Entity<Employee>().ToTable(
    "Employees",
    table => table.IsTemporal(temporal =>
    {
        temporal.UseHistoryTable("EmployeeHistory");
        temporal.HasPeriodStart("ValidFrom");
        temporal.HasPeriodEnd("ValidTo");
    }));

var history = await context.Employees
    .TemporalAll()
    .OrderBy(employee => EF.Property<DateTime>(employee, "ValidFrom"))
    .ToListAsync();

See Temporal Tables and Common Table Expressions for portability rules and complete query examples.

Enable spatial support

After installing the NetTopologySuite package, activate it in provider options:

options.UseMySql(connectionString, serverVersion, mysql =>
    mysql.UseNetTopologySuite());

modelBuilder.Entity<Place>()
    .Property(place => place.Location)
    .HasColumnType("point")
    .HasSrid(4326);

modelBuilder.Entity<Place>()
    .HasIndex(place => place.Location)
    .IsSpatial();

Migrations

The provider uses the standard EF Core tooling:

dotnet ef migrations add InitialCreate
dotnet ef database update

Concurrent migrators are serialized with a dedicated advisory lock. Custom packages can add exact migration-operation handlers without replacing the provider SQL generator; see Migration Operation Handlers.

Existing Pomelo applications should start with Migrating from Pomelo. It distinguishes API changes from schema changes and preserves deployed migration history.

Distributed Caching

Doka.Caching.MySql provides IDistributedCache and IBufferDistributedCache for MySQL and MariaDB. It is a standalone .NET 10 package: neither the EF Core provider nor a DbContext is required.

Install the package:

dotnet package add Doka.Caching.MySql

First, generate the cache table script for an existing database:

using Doka.Caching.MySql;

var script = MySqlCacheSchema.GetCreateScript("app_cache", "DistributedCache");
Console.WriteLine(script);

Review and execute the script separately during deployment. Registration and cache operations never create or upgrade database objects. When replacing another cache implementation, provision a new Doka cache table.

Register the cache with the application's connection string:

using Doka.Caching.MySql;
using Microsoft.Extensions.DependencyInjection;

services.AddDistributedMySqlCache(options =>
{
    options.ConnectionString = connectionString;
    options.SchemaName = "app_cache";
    options.TableName = "DistributedCache";
});

Inject IDistributedCache and pass the operation's cancellation token:

using Microsoft.Extensions.Caching.Distributed;

await cache.SetStringAsync(
    "greeting",
    "Hello from Doka",
    new DistributedCacheEntryOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),
        SlidingExpiration = TimeSpan.FromMinutes(2),
    },
    cancellationToken);

var greeting = await cache.GetStringAsync("greeting", cancellationToken);

Both interfaces resolve the same singleton. Expiration uses database UTC; absolute deadlines cap sliding refreshes. For binary payloads, IBufferDistributedCache reads into caller-owned buffers without an extra value-sized result array. The application identity needs only SELECT, INSERT, UPDATE, and DELETE on the deployed table.

See Distributed Cache for data-source ownership, concurrency, cleanup, schema deployment, and buffer usage.

Compatibility Boundaries

  • MySqlConnector is the only supported ADO.NET driver.
  • Azure Database for MySQL is not yet an advertised compatibility target.
  • Amazon Aurora MySQL is intentionally outside the supported scope.
  • Unsupported query translations fail instead of falling back to client evaluation.
  • EF provider NativeAOT readiness remains blocked by upstream EF Core precompiled-query constraints; trimming is continuously validated. The standalone cache has no EF Core dependency and is verified separately.

See External Limitations for the canonical boundary ledger. Provider-owned gaps have a zero budget and do not belong in that ledger.

Documentation and Support

Use GitHub Issues for reproducible defects, feature requests, compatibility reports, and usage questions. Report suspected vulnerabilities privately through SECURITY.md.

Contributing

Repository setup, test tiers, coding conventions, public API governance, and pull-request requirements are documented in CONTRIBUTING.md. Performance evidence is independent engineering feedback and does not block release publication; its measurement and triage contract lives in the Performance Evidence runbook.

License

MIT -- see LICENSE.

About

Production-focused EF Core 10 provider for MySQL 8.4/9.7 LTS and MariaDB 10.11/11.4/11.8/12.3 LTS, with JSON and complex types, temporal tables, CTEs, HiLo/sequences, NetTopologySuite spatial support, advisory-lock-protected migrations, diagnostics, and engine-aware SQL translation.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages