-
Notifications
You must be signed in to change notification settings - Fork 226
MySQL
Fully supported from v4. 🚀
MySQL is the reason the database reader moved out of the T4 template and into the efrpg dotnet tool. There is
nothing to install into the GAC, nothing to add to machine.config, and no MySQL Connector/NET download.
Install the tool and it works:
dotnet tool install -g Efrpg
If you followed the v3 instructions on this page, you can gacutil /u MySql.Data and delete the
MySQL Data Provider entry you added to machine.config. Nothing in v4 reads them. See
Upgrading from v3 to v4.
Settings.DatabaseType = DatabaseType.MySql;
Settings.TemplateType = TemplateType.EfCore10; // or EfCore9, EfCore8
Settings.GeneratorType = GeneratorType.EfCore;
Settings.OnConfiguration = OnConfiguration.Omit; // important - see below
Settings.ConnectionString = "Server=localhost;Port=3306;Database=Northwind;User Id=root;Password=***;";Your project needs the Pomelo.EntityFrameworkCore.MySql NuGet package.
MariaDB works through the same driver and the same settings. The generator reads information_schema, which
both engines implement.
Pomelo's UseMySql() takes a mandatory ServerVersion argument, which the generator does not emit. If you
leave Settings.OnConfiguration = OnConfiguration.ConnectionString, the generated OnConfiguring will not
compile.
So set:
Settings.OnConfiguration = OnConfiguration.Omit;and configure the context where you register it:
var cs = builder.Configuration.GetConnectionString("MyDbContext");
builder.Services.AddDbContext<MyDbContext>(o => o.UseMySql(cs, ServerVersion.AutoDetect(cs)));ServerVersion.AutoDetect opens a connection at startup to ask the server what it is. If you would rather not
pay for that, or you build in an environment with no database, pin it:
o.UseMySql(cs, new MySqlServerVersion(new Version(8, 0, 36)));
// or, for MariaDB
o.UseMySql(cs, new MariaDbServerVersion(new Version(11, 4, 0)));This is the better pattern anyway, because it keeps your connection string out of generated source. See Settings.OnConfiguration.
This is the biggest conceptual difference from SQL Server, PostgreSQL and Oracle.
MySQL has no separate concept of a schema inside a database. Database=Northwind in your connection string
is the schema, and it is the only one the generator reads. Consequences:
-
Database=is not optional in the connection string. - Tables in another database on the same server are invisible, including through a foreign key that points at them. A cross-database relationship will not appear in the generated model.
-
Settings.PrependSchemaNamehas nothing to prepend, so class names come straight from table names.
If your model genuinely spans two MySQL databases, generate one DbContext per database, each from its own
.tt file, and join across them in your own code.
SELECT on the database you are reading is enough. information_schema filters itself to whatever the
connected user can see, so a read-only account works:
CREATE USER 'efrpg_reader'@'%' IDENTIFIED BY '***';
GRANT SELECT ON northwind.* TO 'efrpg_reader'@'%';| Feature | Supported | Notes |
|---|---|---|
| Tables, views, columns | ✔ | |
| Primary and foreign keys | ✔ | Within the one database |
| Indexes | ✔ | |
AUTO_INCREMENT |
✔ | Generated as ValueGeneratedOnAdd(), which is all Pomelo needs |
| Column defaults | ✔ | |
| Column comments | ✔ |
COLUMN_COMMENT is read and emitted as an XML doc comment |
| Triggers | ✔ | |
| Stored procedures and functions | ✔ | Signatures and parameters, so callers are generated |
| Stored procedure result models | ✘ | See below |
| Sequences | n/a | MySQL has none |
| Synonyms | n/a | MySQL has none |
| Temporal tables | n/a | |
| Cross-database foreign keys | ✘ | See above |
The name and parameters of every stored procedure are read and a caller is generated, but no return model class is produced, because MySQL has no catalogue describing what a procedure returns.
Two options:
-
Point the procedure at an entity you already have:
Settings.StoredProcedureReturnTypes.Add("SalesByYear", "SummaryOfSalesByYear");
-
Write the result class yourself in a partial file and call the procedure with EF Core's
SqlQuery<T>orFromSql.
| MySQL | C# |
|---|---|
tinyint(1) |
bool |
tinyint |
SByte |
tinyint unsigned |
byte |
bit(1) |
bool |
bit |
long |
int unsigned |
long |
bigint unsigned |
decimal |
enum, set
|
string |
json |
string - see JSON column support to map it to a real type |
time |
TimeSpan |
year |
short |
geometry, point, polygon, ... |
NetTopologySuite types |
tinyint(1) mapping to bool is the MySQL convention and matches what Pomelo expects. If you really do store
-128..127 in a tinyint(1), declare it as tinyint(4) in the database, or override the column in
Settings.UpdateColumn.
Spatial columns need NetTopologySuite and o.UseMySql(cs, serverVersion, x => x.UseNetTopologySuite()). If
you do not want them at all, set Settings.DisableGeographyTypes = true.
On Linux, MySQL table names are case sensitive by default (lower_case_table_names=0); on Windows and macOS
they are not. Your regex filters run against the real name as MySQL reports it, so a filter that works on a
developer's Windows machine can silently match nothing on a Linux CI agent. Use RegexOptions.IgnoreCase:
FilterSettings.TableFilters.Add(new RegexIncludeFilter(
new Regex("^order.*", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(200))));- Settings A-Z - every setting, with a page each
- Common Settings Types Explained
- Settings Callbacks
- Settings runtime values and helpers
- Filtering
- Full Control Over the Generated Code
- Enum Generation from Table Data
- Owned Entities
- JSON column support
- Global Query Filters
- Extended Property Names Feature
- Partial Properties
- File-Scoped Namespaces
- Data Annotations
- Spatial Types
- HierarchyId
- RowVersion and TimeStamp columns
- Lazy Loading
- Stored proc result sets
- Custom File-Based Templates
- Extra entities via partial classes
- INotifyPropertyChanged
- Syntax colour for T4