Skip to content

Commit 4ab5a6e

Browse files
committed
Update Migrations with Multiple Providers
Fixes #1554
1 parent 8369a1c commit 4ab5a6e

16 files changed

+1021
-36
lines changed

entity-framework/core/managing-schemas/migrations/providers.md

+35-36
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,39 @@
22
title: Migrations with Multiple Providers - EF Core
33
description: Using migrations to manage database schemas when targeting multiple database providers with Entity Framework Core
44
author: bricelam
5-
ms.date: 11/08/2017
5+
ms.date: 10/29/2020
66
uid: core/managing-schemas/migrations/providers
77
---
88
# Migrations with Multiple Providers
99

10-
The [EF Core Tools][1] only scaffold migrations for the active provider. Sometimes, however, you may want to use more than one provider (for example Microsoft SQL Server and SQLite) with your DbContext. There are two ways to handle this with Migrations. You can maintain two sets of migrations--one for each provider--or merge them into a single set
11-
that can work on both.
10+
The [EF Core Tools](xref:core/miscellaneous/cli/index) only scaffold migrations for the active provider. Sometimes, however, you may want to use more than one provider (for example Microsoft SQL Server and SQLite) with your DbContext. Handle this by maintaining multiple sets of migrations--one for each provider--and adding a migration to each for every model change.
1211

13-
## Two migration sets
12+
## Using multiple context types
1413

15-
In the first approach, you generate two migrations for each model change.
16-
17-
One way to do this is to put each migration set [in a separate assembly][2] and manually switch the active provider (and migrations assembly) between adding the two migrations.
18-
19-
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.
14+
One way to create multiple migration sets is to use one DbContext type per provider.
2015

2116
```csharp
22-
class MySqliteDbContext : MyDbContext
17+
class SqliteBlogContext : BlogContext
2318
{
2419
protected override void OnConfiguring(DbContextOptionsBuilder options)
2520
=> options.UseSqlite("Data Source=my.db");
2621
}
2722
```
2823

29-
> [!NOTE]
30-
> Since each migration set uses its own DbContext types, this approach doesn't require using a separate migrations
31-
> assembly.
32-
33-
When adding new migration, specify the context types.
24+
Specify the context type when adding new migrations.
3425

3526
### [.NET Core CLI](#tab/dotnet-core-cli)
3627

3728
```dotnetcli
38-
dotnet ef migrations add InitialCreate --context MyDbContext --output-dir Migrations/SqlServerMigrations
39-
dotnet ef migrations add InitialCreate --context MySqliteDbContext --output-dir Migrations/SqliteMigrations
29+
dotnet ef migrations add InitialCreate --context BlogContext --output-dir Migrations/SqlServerMigrations
30+
dotnet ef migrations add InitialCreate --context SqliteBlogContext --output-dir Migrations/SqliteMigrations
4031
```
4132

4233
### [Visual Studio](#tab/vs)
4334

4435
```powershell
45-
Add-Migration InitialCreate -Context MyDbContext -OutputDir Migrations\SqlServerMigrations
46-
Add-Migration InitialCreate -Context MySqliteDbContext -OutputDir Migrations\SqliteMigrations
36+
Add-Migration InitialCreate -Context BlogContext -OutputDir Migrations\SqlServerMigrations
37+
Add-Migration InitialCreate -Context SqliteBlogContext -OutputDir Migrations\SqliteMigrations
4738
```
4839

4940
***
@@ -52,28 +43,36 @@ Add-Migration InitialCreate -Context MySqliteDbContext -OutputDir Migrations\Sql
5243
> You don't need to specify the output directory for subsequent migrations since they are created as siblings to the
5344
> last one.
5445
55-
## One migration set
46+
## Using one context type
5647

57-
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.
48+
It's also possible to use one DbContext type. This currently requires moving the migrations into a separate assembly. Please refer to [Using a Separate Migrations Project](xref:core/managing-schemas/migrations/projects) for instructions on setting up your projects.
5849

59-
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.
50+
Starting in EF Core 5.0, you can pass arguments into the app from the tools. This can enable a more streamlined workflow that avoids having to make manual changes to the project when running the tools.
6051

61-
```csharp
62-
Id = table.Column<int>(nullable: false)
63-
.Annotation("SqlServer:ValueGenerationStrategy",
64-
SqlServerValueGenerationStrategy.IdentityColumn)
65-
.Annotation("Sqlite:Autoincrement", true),
52+
Here's one pattern that works well when using a [Generic Host](/dotnet/core/extensions/generic-host).
53+
54+
[!code-csharp[](../../../../samples/core/Schemas/TwoProjectMigrations/WorkerService1/Program.cs#snippet_CreateHostBuilder)]
55+
56+
Since the default host builder reads configuration from command-line arguments, you can specify the provider when running the tools.
57+
58+
### [.NET Core CLI](#tab/dotnet-core-cli)
59+
60+
```dotnetcli
61+
dotnet ef migrations add MyMigration --project ../SqlServerMigrations -- --provider SqlServer
62+
dotnet ef migrations add MyMigration --project ../SqliteMigrations -- --provider Sqlite
6663
```
6764

68-
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:
65+
> [!TIP]
66+
> The `--` token directs `dotnet ef` to treat everything that follows as an argument and not try to parse them as options. Any extra arguments not used by `dotnet ef` are forwarded to the app.
6967
70-
```csharp
71-
if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer")
72-
{
73-
migrationBuilder.CreateSequence(
74-
name: "EntityFrameworkHiLoSequence");
75-
}
68+
### [Visual Studio](#tab/vs)
69+
70+
```powershell
71+
Add-Migration MyMigration -Args "--provider SqlServer"
72+
Add-Migration MyMigration -Args "--provider Sqlite"
7673
```
7774

78-
[1]: xref:core/miscellaneous/cli/index
79-
[2]: xref:core/managing-schemas/migrations/projects
75+
***
76+
77+
> [!NOTE]
78+
> The ability to specify additional arguments for the app was added in EF Core 5.0. If you're using an older version, specify configuration values with environment variables instead.

samples/core/Samples.sln

+24
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Schemas", "Schemas", "{0BFE
9797
EndProject
9898
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Migrations", "Schemas\Migrations\Migrations.csproj", "{D381F3EE-FEA4-4777-B9CA-7EE7E4C3289E}"
9999
EndProject
100+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TwoProjectMigrations", "TwoProjectMigrations", "{A717B3B8-DB2C-40A5-8C31-1DE1FA03CF40}"
101+
EndProject
102+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkerService1", "Schemas\TwoProjectMigrations\WorkerService1\WorkerService1.csproj", "{AC03251E-422C-4101-9EC8-D40A9A76AD64}"
103+
EndProject
104+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqliteMigrations", "Schemas\TwoProjectMigrations\SqliteMigrations\SqliteMigrations.csproj", "{00A36564-7244-484A-A8C4-C56D343E255D}"
105+
EndProject
106+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlServerMigrations", "Schemas\TwoProjectMigrations\SqlServerMigrations\SqlServerMigrations.csproj", "{9AD894B4-B5CF-44D6-BCCB-04D35CD07790}"
107+
EndProject
100108
Global
101109
GlobalSection(SolutionConfigurationPlatforms) = preSolution
102110
Debug|Any CPU = Debug|Any CPU
@@ -263,6 +271,18 @@ Global
263271
{D381F3EE-FEA4-4777-B9CA-7EE7E4C3289E}.Debug|Any CPU.Build.0 = Debug|Any CPU
264272
{D381F3EE-FEA4-4777-B9CA-7EE7E4C3289E}.Release|Any CPU.ActiveCfg = Release|Any CPU
265273
{D381F3EE-FEA4-4777-B9CA-7EE7E4C3289E}.Release|Any CPU.Build.0 = Release|Any CPU
274+
{AC03251E-422C-4101-9EC8-D40A9A76AD64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
275+
{AC03251E-422C-4101-9EC8-D40A9A76AD64}.Debug|Any CPU.Build.0 = Debug|Any CPU
276+
{AC03251E-422C-4101-9EC8-D40A9A76AD64}.Release|Any CPU.ActiveCfg = Release|Any CPU
277+
{AC03251E-422C-4101-9EC8-D40A9A76AD64}.Release|Any CPU.Build.0 = Release|Any CPU
278+
{00A36564-7244-484A-A8C4-C56D343E255D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
279+
{00A36564-7244-484A-A8C4-C56D343E255D}.Debug|Any CPU.Build.0 = Debug|Any CPU
280+
{00A36564-7244-484A-A8C4-C56D343E255D}.Release|Any CPU.ActiveCfg = Release|Any CPU
281+
{00A36564-7244-484A-A8C4-C56D343E255D}.Release|Any CPU.Build.0 = Release|Any CPU
282+
{9AD894B4-B5CF-44D6-BCCB-04D35CD07790}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
283+
{9AD894B4-B5CF-44D6-BCCB-04D35CD07790}.Debug|Any CPU.Build.0 = Debug|Any CPU
284+
{9AD894B4-B5CF-44D6-BCCB-04D35CD07790}.Release|Any CPU.ActiveCfg = Release|Any CPU
285+
{9AD894B4-B5CF-44D6-BCCB-04D35CD07790}.Release|Any CPU.Build.0 = Release|Any CPU
266286
EndGlobalSection
267287
GlobalSection(SolutionProperties) = preSolution
268288
HideSolutionNode = FALSE
@@ -302,6 +322,10 @@ Global
302322
{0F8779BE-F32A-42A9-B2D7-1974A1D9DD09} = {85AFD7F1-6943-40FE-B8EC-AA9DBB42CCA6}
303323
{EE7AF867-1BC6-4C80-8856-B4DDB975E546} = {85AFD7F1-6943-40FE-B8EC-AA9DBB42CCA6}
304324
{D381F3EE-FEA4-4777-B9CA-7EE7E4C3289E} = {0BFEC418-1A37-4960-8488-EA8AFB916EB9}
325+
{A717B3B8-DB2C-40A5-8C31-1DE1FA03CF40} = {0BFEC418-1A37-4960-8488-EA8AFB916EB9}
326+
{AC03251E-422C-4101-9EC8-D40A9A76AD64} = {A717B3B8-DB2C-40A5-8C31-1DE1FA03CF40}
327+
{00A36564-7244-484A-A8C4-C56D343E255D} = {A717B3B8-DB2C-40A5-8C31-1DE1FA03CF40}
328+
{9AD894B4-B5CF-44D6-BCCB-04D35CD07790} = {A717B3B8-DB2C-40A5-8C31-1DE1FA03CF40}
305329
EndGlobalSection
306330
GlobalSection(ExtensibilityGlobals) = postSolution
307331
SolutionGuid = {20C98D35-54EF-46A6-8F3B-1855C1AE4F70}

samples/core/Schemas/TwoProjectMigrations/SqlServerMigrations/20200915225801_InitialCreate.Designer.cs

+141
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using Microsoft.EntityFrameworkCore.Migrations;
2+
3+
namespace SqlServerMigrations
4+
{
5+
public partial class InitialCreate : Migration
6+
{
7+
protected override void Up(MigrationBuilder migrationBuilder)
8+
{
9+
migrationBuilder.CreateTable(
10+
name: "Blogs",
11+
columns: table => new
12+
{
13+
Id = table.Column<int>(type: "int", nullable: false)
14+
.Annotation("SqlServer:Identity", "1, 1"),
15+
Source = table.Column<string>(type: "nvarchar(max)", nullable: true),
16+
Title = table.Column<string>(type: "nvarchar(max)", nullable: true)
17+
},
18+
constraints: table =>
19+
{
20+
table.PrimaryKey("PK_Blogs", x => x.Id);
21+
});
22+
23+
migrationBuilder.CreateTable(
24+
name: "People",
25+
columns: table => new
26+
{
27+
Id = table.Column<int>(type: "int", nullable: false)
28+
.Annotation("SqlServer:Identity", "1, 1"),
29+
Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
30+
Email = table.Column<string>(type: "nvarchar(450)", nullable: true)
31+
},
32+
constraints: table =>
33+
{
34+
table.PrimaryKey("PK_People", x => x.Id);
35+
});
36+
37+
migrationBuilder.CreateTable(
38+
name: "Posts",
39+
columns: table => new
40+
{
41+
Id = table.Column<int>(type: "int", nullable: false)
42+
.Annotation("SqlServer:Identity", "1, 1"),
43+
BlogId = table.Column<int>(type: "int", nullable: false),
44+
Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
45+
Content = table.Column<string>(type: "nvarchar(max)", nullable: true),
46+
AuthorId = table.Column<int>(type: "int", nullable: false),
47+
Discriminator = table.Column<string>(type: "nvarchar(max)", nullable: false),
48+
MediaUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
49+
MediaType = table.Column<string>(type: "nvarchar(max)", nullable: true)
50+
},
51+
constraints: table =>
52+
{
53+
table.PrimaryKey("PK_Posts", x => x.Id);
54+
table.ForeignKey(
55+
name: "FK_Posts_Blogs_BlogId",
56+
column: x => x.BlogId,
57+
principalTable: "Blogs",
58+
principalColumn: "Id",
59+
onDelete: ReferentialAction.Cascade);
60+
table.ForeignKey(
61+
name: "FK_Posts_People_AuthorId",
62+
column: x => x.AuthorId,
63+
principalTable: "People",
64+
principalColumn: "Id",
65+
onDelete: ReferentialAction.Cascade);
66+
});
67+
68+
migrationBuilder.CreateIndex(
69+
name: "IX_People_Email",
70+
table: "People",
71+
column: "Email",
72+
unique: true,
73+
filter: "[Email] IS NOT NULL");
74+
75+
migrationBuilder.CreateIndex(
76+
name: "IX_Posts_AuthorId",
77+
table: "Posts",
78+
column: "AuthorId");
79+
80+
migrationBuilder.CreateIndex(
81+
name: "IX_Posts_BlogId",
82+
table: "Posts",
83+
column: "BlogId");
84+
}
85+
86+
protected override void Down(MigrationBuilder migrationBuilder)
87+
{
88+
migrationBuilder.DropTable(
89+
name: "Posts");
90+
91+
migrationBuilder.DropTable(
92+
name: "Blogs");
93+
94+
migrationBuilder.DropTable(
95+
name: "People");
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)