Skip to content

Commit

Permalink
Document passing design-time args
Browse files Browse the repository at this point in the history
Additional changes:

- Add tips about EXEC (fixes dotnet#2561)
- Add a tab for Get-Migration
- Update custom operation samples (fixes dotnet#1574)
- Fix some bad PMC examples (fixes dotnet#2296)
- Add examples using Name= (fixes dotnet#2145)

Fixes dotnet#1050
  • Loading branch information
bricelam committed Oct 27, 2020
1 parent a2ba0cf commit 41f3772
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 112 deletions.
18 changes: 17 additions & 1 deletion entity-framework/core/managing-schemas/migrations/managing.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Managing Migrations - EF Core
description: Adding, removing and otherwise managing database schema migrations with Entity Framework Core
author: bricelam
ms.date: 05/06/2020
ms.date: 10/27/2020
uid: core/managing-schemas/migrations/managing
---
# Managing Migrations
Expand Down Expand Up @@ -147,6 +147,9 @@ migrationBuilder.Sql(
RETURN @LastName + @FirstName;')");
```

> [!TIP]
> `EXEC` is used when a statement must be the first or only one in a SQL batch. It can also be used to work around parser errors in idempotent migration scripts that can occur when referenced columns don't currently exist on a table.
This can be used to manage any aspect of your database, including:

* Stored procedures
Expand Down Expand Up @@ -186,10 +189,23 @@ After removing the migration, you can make the additional model changes and add

You can list all existing migrations as follows:

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

```dotnetcli
dotnet ef migrations list
```

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

> [!NOTE]
> This command was added in EF Core 5.0.
```powershell
Get-Migration
```

***

## Resetting all migrations

In some extreme cases, it may be necessary to remove all migrations and start over. This can be easily done by deleting your **Migrations** folder and dropping your database; at that point you can create a new initial migration, which will contain you entire current schema.
Expand Down
109 changes: 10 additions & 99 deletions entity-framework/core/managing-schemas/migrations/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Custom Migrations Operations - EF Core
description: Managing custom and raw SQL migrations for database schema management with Entity Framework Core
author: bricelam
ms.date: 11/07/2017
ms.date: 10/27/2020
uid: core/managing-schemas/migrations/operations
---
# Custom Migrations Operations
Expand All @@ -19,120 +19,31 @@ migrationBuilder.CreateUser("SQLUser1", "Password");

The easiest way to implement a custom operation is to define an extension method that calls `MigrationBuilder.Sql()`. Here is an example that generates the appropriate Transact-SQL.

```csharp
static MigrationBuilder CreateUser(
this MigrationBuilder migrationBuilder,
string name,
string password)
=> migrationBuilder.Sql($"CREATE USER {name} WITH PASSWORD '{password}';");
```
[!code-csharp[](../../../../samples/core/Schemas/Migrations/CustomOperationSql.cs#snippet_CustomOperationSql)]

> [!TIP]
> Use the `EXEC` function when a statement must be the first or only one in a SQL batch. It might also be needed to work around parser errors in idempotent migration scripts that can occur when referenced columns don't currently exist on a table.
If your migrations need to support multiple database providers, you can use the `MigrationBuilder.ActiveProvider` property. Here's an example supporting both Microsoft SQL Server and PostgreSQL.

```csharp
static MigrationBuilder CreateUser(
this MigrationBuilder migrationBuilder,
string name,
string password)
{
switch (migrationBuilder.ActiveProvider)
{
case "Npgsql.EntityFrameworkCore.PostgreSQL":
return migrationBuilder
.Sql($"CREATE USER {name} WITH PASSWORD '{password}';");

case "Microsoft.EntityFrameworkCore.SqlServer":
return migrationBuilder
.Sql($"CREATE USER {name} WITH PASSWORD = '{password}';");
}

return migrationBuilder;
}
```
[!code-csharp[](../../../../samples/core/Schemas/Migrations/CustomOperationMultiSql.cs#snippet_CustomOperationMultiSql)]

This approach only works if you know every provider where your custom operation will be applied.

## Using a MigrationOperation

To decouple the custom operation from the SQL, you can define your own `MigrationOperation` to represent it. The operation is then passed to the provider so it can determine the appropriate SQL to generate.

```csharp
class CreateUserOperation : MigrationOperation
{
public string Name { get; set; }
public string Password { get; set; }
}
```
[!code-csharp[](../../../../samples/core/Schemas/Migrations/CustomOperation.cs#snippet_CreateUserOperation)]

With this approach, the extension method just needs to add one of these operations to `MigrationBuilder.Operations`.

```csharp
static MigrationBuilder CreateUser(
this MigrationBuilder migrationBuilder,
string name,
string password)
{
migrationBuilder.Operations.Add(
new CreateUserOperation
{
Name = name,
Password = password
});

return migrationBuilder;
}
```
[!code-csharp[](../../../../samples/core/Schemas/Migrations/CustomOperation.cs#snippet_MigrationBuilderExtension)]

This approach requires each provider to know how to generate SQL for this operation in their `IMigrationsSqlGenerator` service. Here is an example overriding the SQL Server's generator to handle the new operation.

```csharp
class MyMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
public MyMigrationsSqlGenerator(
MigrationsSqlGeneratorDependencies dependencies,
IMigrationsAnnotationProvider migrationsAnnotations)
: base(dependencies, migrationsAnnotations)
{
}

protected override void Generate(
MigrationOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
if (operation is CreateUserOperation createUserOperation)
{
Generate(createUserOperation, builder);
}
else
{
base.Generate(operation, model, builder);
}
}

private void Generate(
CreateUserOperation operation,
MigrationCommandListBuilder builder)
{
var sqlHelper = Dependencies.SqlGenerationHelper;
var stringMapping = Dependencies.TypeMappingSource.FindMapping(typeof(string));

builder
.Append("CREATE USER ")
.Append(sqlHelper.DelimitIdentifier(operation.Name))
.Append(" WITH PASSWORD = ")
.Append(stringMapping.GenerateSqlLiteral(operation.Password))
.AppendLine(sqlHelper.StatementTerminator)
.EndCommand();
}
}
```
[!code-csharp[](../../../../samples/core/Schemas/Migrations/CustomOperation.cs#snippet_MigrationsSqlGenerator)]

Replace the default migrations sql generator service with the updated one.

```csharp
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options
.UseSqlServer(connectionString)
.ReplaceService<IMigrationsSqlGenerator, MyMigrationsSqlGenerator>();
```
[!code-csharp[](../../../../samples/core/Schemas/Migrations/CustomOperation.cs#snippet_OnConfiguring)]
26 changes: 24 additions & 2 deletions entity-framework/core/miscellaneous/cli/dbcontext-creation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Design-time DbContext Creation - EF Core
description: Strategies for creating a design-time DbContext with Entity Framework Core
author: bricelam
ms.date: 09/16/2019
ms.date: 10/27/2020
uid: core/miscellaneous/cli/dbcontext-creation
---
# Design-time DbContext Creation
Expand Down Expand Up @@ -39,7 +39,29 @@ You can also tell the tools how to create your DbContext by implementing the `ID
> This is fixed in EFCore 5.0 and any additional design-time arguments
> are passed into the application through that parameter.
A design-time factory can be especially useful if you need to configure the DbContext differently for design time than at run time, if the `DbContext` constructor takes additional parameters are not registered in DI, if you are not using DI at all, or if for some reason you prefer not to have a `BuildWebHost` method in your ASP.NET Core application's `Main` class.
A design-time factory can be especially useful if you need to configure the DbContext differently for design time than at run time, if the `DbContext` constructor takes additional parameters are not registered in DI, if you are not using DI at all, or if for some reason you prefer not to have a `CreateHostBuilder` method in your ASP.NET Core application's `Main` class.

## Args

Both IDesignTimeDbContextFactory.CreateDbContext and Program.CreateHostBuilder accept command line arguments.

Starting in EF Core 5.0, you can specify these arguments from the tools:

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

```dotnetcli
dotnet ef database update -- --environment Production
```

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.

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

```powershell
Update-Database -Args '--environment Production'
```

***

[1]: xref:core/managing-schemas/migrations/index
[2]: xref:core/miscellaneous/configuring-dbcontext
Expand Down
20 changes: 18 additions & 2 deletions entity-framework/core/miscellaneous/cli/dotnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: EF Core tools reference (.NET CLI) - EF Core
description: Reference guide for the Entity Framework Core .NET Core CLI tools
author: bricelam
ms.date: 10/13/2020
ms.date: 10/27/2020
uid: core/miscellaneous/cli/dotnet
---

Expand Down Expand Up @@ -93,7 +93,16 @@ Why is a dummy project required? As mentioned earlier, the tools have to execute

### ASP.NET Core environment

To specify the environment for ASP.NET Core projects, set the **ASPNETCORE_ENVIRONMENT** environment variable before running commands.
To specify [the environment](/aspnet/core/fundamentals/environments) for ASP.NET Core projects, set the **ASPNETCORE_ENVIRONMENT** environment variable before running commands.

Starting in EF Core 5.0, additional arguments can also be passed into Program.CreateHostBuilder allowing you to specify the environment on the command-line:

```dotnetcli
dotnet ef database update -- --environment Production
```

> [!TIP]
> 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.
## Common options

Expand Down Expand Up @@ -206,6 +215,13 @@ The following example scaffolds only selected tables and creates the context in
dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models -t Blog -t Post --context-dir Context -c BlogContext --context-namespace New.Namespace
```

The following example reads the connection string from the project's configuration set using the [Secret Manager tool](/aspnet/core/security/app-secrets#secret-manager).

```dotnetcli
dotnet user-secrets set ConnectionStrings.Blogging "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Blogging"
dotnet ef dbcontext scaffold Name=ConnectionStrings.Blogging Microsoft.EntityFrameworkCore.SqlServer
```

## dotnet ef dbcontext script

Generates a SQL script from the DbContext. Bypasses any migrations. Added in EF Core 3.0.
Expand Down
28 changes: 20 additions & 8 deletions entity-framework/core/miscellaneous/cli/powershell.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: EF Core tools reference (Package Manager Console) - EF Core
description: Reference guide for the Entity Framework Core Visual Studio Package Manager Console
author: bricelam
ms.date: 10/13/2020
ms.date: 10/27/2020
uid: core/miscellaneous/cli/powershell
---
# Entity Framework Core tools reference - Package Manager Console in Visual Studio
Expand Down Expand Up @@ -84,7 +84,13 @@ Why is a dummy project required? As mentioned earlier, the tools have to execute

### ASP.NET Core environment

To specify the environment for ASP.NET Core projects, set **env:ASPNETCORE_ENVIRONMENT** before running commands.
To specify [the environment](/aspnet/core/fundamentals/environments) for ASP.NET Core projects, set **env:ASPNETCORE_ENVIRONMENT** before running commands.

Starting in EF Core 5.0, additional arguments can also be passed into Program.CreateHostBuilder allowing you to specify the environment on the command-line:

```powershell
Update-Database -Args '--environment Production'
```

## Common parameters

Expand Down Expand Up @@ -197,6 +203,12 @@ Example that scaffolds only selected tables and creates the context in a separat
Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Tables "Blog","Post" -ContextDir Context -Context BlogContext -ContextNamespace New.Namespace
```

The following example reads the connection string from the project's configuration possibly set using the [Secret Manager tool](/aspnet/core/security/app-secrets#secret-manager).

```dotnetcli
dotnet ef dbcontext scaffold Name=ConnectionStrings.Blogging Microsoft.EntityFrameworkCore.SqlServer
```

## Script-DbContext

Generates a SQL script from the DbContext. Bypasses any migrations. Added in EF Core 3.0.
Expand Down Expand Up @@ -228,16 +240,16 @@ The [common parameters](#common-parameters) are listed above.
> [!TIP]
> The To, From, and Output parameters support tab-expansion.
The following example creates a script for the InitialCreate migration, using the migration name.
The following example creates a script for the InitialCreate migration (from a database without any migrations), using the migration name.

```powershell
Script-Migration -To InitialCreate
Script-Migration 0 InitialCreate
```

The following example creates a script for all migrations after the InitialCreate migration, using the migration ID.

```powershell
Script-Migration -From 20180904195021_InitialCreate
Script-Migration 20180904195021_InitialCreate
```

## Update-Database
Expand All @@ -257,14 +269,14 @@ The [common parameters](#common-parameters) are listed above.
The following example reverts all migrations.

```powershell
Update-Database -Migration 0
Update-Database 0
```

The following examples update the database to a specified migration. The first uses the migration name and the second uses the migration ID and a specified connection:

```powershell
Update-Database -Migration InitialCreate
Update-Database -Migration 20180904195021_InitialCreate -Connection your_connection_string
Update-Database InitialCreate
Update-Database 20180904195021_InitialCreate -Connection your_connection_string
```

## Additional resources
Expand Down
Loading

0 comments on commit 41f3772

Please sign in to comment.