Reputation: 23
I need to support Migration for MariaDb and SqlServer in a project. Currently, I edited migration files which created by add-migration command. You can see some part of migration file.
migrationBuilder.CreateTable(
name: "Tbl1",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1")
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(maxLength: 100, nullable: true),
Measurement = table.Column<string>(maxLength: 100, nullable: true),
},
constraints: table =>
{
table.PrimaryKey("PK_Tbl1", x => x.Id);
});
With that change, my migration files can support both database types (MariaDb and SqlServer). But, I have to set and use the same ColumnType for both. I would like to know is there any best practice provided by Entityframework to support multiple database types or not?
Upvotes: 1
Views: 1420
Reputation: 5254
Take a look at Migrations with Multiple Providers which discusses exactly that scenario with the following two approaches:
Two migration sets
In the first approach, you generate two migrations for each model change.
One way to do this is to put each migration set in a separate assembly and manually switch the active provider (and migrations assembly) between adding the two migrations.
Another approach that makes working with the tools easier is to create a new type that derives from your DbContext and overrides the active provider. This type is used at design time when adding or applying migrations.
class MySqliteDbContext : MyDbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite("Data Source=my.db");
}
Note
Since each migration set uses its own DbContext types, this approach doesn't require using a separate migrations assembly.
dotnet ef migrations add InitialCreate --context MyDbContext --output-dir Migrations/SqlServerMigrations
dotnet ef migrations add InitialCreate --context MySqliteDbContext --output-dir Migrations/SqliteMigrations
One migration set
If you don't like having two sets of migrations, you can manually combine them into a single set that can be applied to both providers.
Annotations can coexist since a provider ignores any annotations that it doesn't understand. For example, a primary key column that works with both Microsoft SQL Server and SQLite might look like this.
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn)
.Annotation("Sqlite:Autoincrement", true),
If operations can be applied only for one provider, or they're different between providers, use the ActiveProvider property to determine which provider is active:
if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer")
{
migrationBuilder.CreateSequence(
name: "EntityFrameworkHiLoSequence");
}
Also see Implement alternatives to the current behavior to always throw, if a schema has been set for an object #982 and references about how to handle the case, that MySQL und MariaDB don't know what a SQL Server Schema (e.g. dbo
) is (assuming you are using
Pomelo.EntityFrameworkCore.MySql as the provider to access the MariaDB database).
Here are some code options:
// Throw an exception, if a schema is being used. This is the default.
options.UseMySql(myConnectionString, b => b.SchemaBehavior(MySqlSchemaBehavior.Throw))
// Silently ignore any schema definitions.
options.UseMySql(myConnectionString, b => b.SchemaBehavior(MySqlSchemaBehavior.Ignore))
// Use the specified translator delegate to translate from an input schema and object name to
// an output object name whenever a schema is being used.
options.UseMySql(myConnectionString, b => b.SchemaBehavior(MySqlSchemaBehavior.Translate,
(schema, entity) => $"{schema ?? "dbo"}_{entity}"))
Upvotes: 2