diff --git a/src/Microsoft.EntityFrameworkCore.Commands/Design/DbContextOperations.cs b/src/Microsoft.EntityFrameworkCore.Commands/Design/DbContextOperations.cs index 6a39f8e6a80..0f382996a3b 100644 --- a/src/Microsoft.EntityFrameworkCore.Commands/Design/DbContextOperations.cs +++ b/src/Microsoft.EntityFrameworkCore.Commands/Design/DbContextOperations.cs @@ -47,6 +47,18 @@ public DbContextOperations( _runtimeServices = startup.ConfigureServices(); } + public virtual void DropDatabase([CanBeNull] string contextType, [NotNull] Func confirmCheck) + { + using (var context = CreateContext(contextType)) + { + var connection = context.Database.GetDbConnection(); + if (confirmCheck(connection.Database, connection.DataSource)) + { + context.Database.EnsureDeleted(); + } + } + } + public virtual DbContext CreateContext([CanBeNull] string contextType) => CreateContext(FindContextType(contextType).Value); @@ -201,5 +213,6 @@ private static IDictionary> FilterTypes( || string.Equals(t.Key.AssemblyQualifiedName, name, comparisonType)) .ToDictionary(t => t.Key, t => t.Value); } + } } \ No newline at end of file diff --git a/src/Microsoft.EntityFrameworkCore.Commands/Design/OperationExecutor.cs b/src/Microsoft.EntityFrameworkCore.Commands/Design/OperationExecutor.cs index 01e4455a589..3bcba04e15c 100644 --- a/src/Microsoft.EntityFrameworkCore.Commands/Design/OperationExecutor.cs +++ b/src/Microsoft.EntityFrameworkCore.Commands/Design/OperationExecutor.cs @@ -342,6 +342,27 @@ private IEnumerable ReverseEngineerImpl( } } + public class DropDatabase : OperationBase + { + public DropDatabase( + [NotNull] OperationExecutor executor, + [NotNull] object resultHandler, + [NotNull] IDictionary args) + : base(resultHandler) + { + Check.NotNull(executor, nameof(executor)); + Check.NotNull(args, nameof(args)); + + var contextType = (string)args["contextType"]; + var confirmCheck = (Func)args["confirmCheck"]; + + Execute(() => executor.DropDatabaseImpl(contextType, confirmCheck)); + } + } + + private void DropDatabaseImpl(string contextType, Func confirmCheck) + => _contextOperations.Value.DropDatabase(contextType, confirmCheck); + public abstract class OperationBase : MarshalByRefObject { private readonly IOperationResultHandler _resultHandler; diff --git a/src/Microsoft.EntityFrameworkCore.Commands/tools/EntityFramework.psd1 b/src/Microsoft.EntityFrameworkCore.Commands/tools/EntityFramework.psd1 index 67a8387ff28..3b74e13ef9b 100644 --- a/src/Microsoft.EntityFrameworkCore.Commands/tools/EntityFramework.psd1 +++ b/src/Microsoft.EntityFrameworkCore.Commands/tools/EntityFramework.psd1 @@ -65,7 +65,8 @@ 'Script-Migration', 'Remove-Migration', 'Enable-Migrations', - 'Scaffold-DbContext' + 'Scaffold-DbContext', + 'Drop-Database' ) # Cmdlets to export from this module diff --git a/src/Microsoft.EntityFrameworkCore.Commands/tools/EntityFramework.psm1 b/src/Microsoft.EntityFrameworkCore.Commands/tools/EntityFramework.psm1 index 5b9459adbf8..6975343e05b 100644 --- a/src/Microsoft.EntityFrameworkCore.Commands/tools/EntityFramework.psm1 +++ b/src/Microsoft.EntityFrameworkCore.Commands/tools/EntityFramework.psm1 @@ -50,7 +50,7 @@ function Use-DbContext { if ($candidates.length -gt 1 -and $exactMatch -is "String") { $candidates = $exactMatch } - + if ($candidates.length -lt 1) { throw "No DbContext named '$Context' was found" } elseif ($candidates.length -gt 1 -and !($candidates -is "String")) { @@ -498,7 +498,7 @@ function Scaffold-DbContext { $artifacts | %{ $dteProject.ProjectItems.AddFromFile($_) | Out-Null } $DTE.ItemOperations.OpenFile($artifacts[0]) | Out-Null - + ShowConsole } } @@ -512,6 +512,71 @@ function Enable-Migrations { Write-Warning 'Enable-Migrations is obsolete. Use Add-Migration to start using Migrations.' } +# +# Drop-Database +# + +Register-TabExpansion Drop-Database @{ + Context = { param ($tabExpansionContext) GetContextTypes $tabExpansionContext.Project $tabExpansionContext.StartupProject $tabExpansionContext.Environment } + Project = { GetProjects } + StartupProject = { GetProjects } +} + +<# +.SYNOPSIS + Drops the database. + +.DESCRIPTION + Drops the database. + +.PARAMETER Context + Specifies the DbContext to use. If omitted, the default DbContext is used. + +.PARAMETER Project + Specifies the project to use. If omitted, the default project is used. + +.PARAMETER StartupProject + Specifies the startup project to use. If omitted, the solution's startup project is used. + +.PARAMETER Environment + Specifies the environment to use. If omitted, "Development" is used. + +.LINK + about_EntityFramework +#> +function Drop-Database { + [CmdletBinding(ConfirmImpact = 'High', SupportsShouldProcess = $true, PositionalBinding = $false)] + param ( + [string] $Context, + [string] $Project, + [string] $StartupProject, + [string] $Environment) + + $values = ProcessCommonParameters $StartupProject $Project $Context + $dteStartupProject = $values.StartupProject + $dteProject = $values.Project + $contextTypeName = $values.ContextTypeName + + if (IsDotNetProject $dteProject) { + # TODO: Show database information + if ($PSCmdlet.ShouldProcess('the database')) { + $options = ProcessCommonDotnetParameters $dteProject $dteStartupProject $Environment $contextTypeName + $options += "--force" + InvokeDotNetEf $dteProject database drop @options | Out-Null + } + } else { + InvokeOperation $dteStartupProject $Environment $dteProject DropDatabase @{ + contextType = $contextTypeName + # TODO: Get database information then prompt before invoking DropDatabase + confirmCheck = [Func[string, string, bool]]{ + param ($database, $dataSource) + + return $PSCmdlet.ShouldProcess("$database on $dataSource") + } + } + } +} + # # (Private Helpers) # @@ -535,7 +600,7 @@ function GetContextTypes($projectName, $startupProjectName, $environment) { $project = $values.Project if (IsDotNetProject $startupProject) { - $types = InvokeDotNetEf $startupProject -Json dbcontext list + $types = InvokeDotNetEf $startupProject -Json dbcontext list return $types | %{ $_.fullName } } else { $contextTypes = InvokeOperation $startupProject $environment $project GetContextTypes -skipBuild @@ -640,7 +705,7 @@ function InvokeDotNetEf($project, [switch] $Json) { $arguments += "--json" } else { # TODO better json output parsing so we don't need to suppress verbose output - $arguments = ,"--verbose" + $arguments + $arguments = ,"--verbose" + $arguments } $command = "ef $($arguments -join ' ')" try { diff --git a/src/Microsoft.EntityFrameworkCore.Commands/tools/about_EntityFramework.help.txt b/src/Microsoft.EntityFrameworkCore.Commands/tools/about_EntityFramework.help.txt index c570efdefb0..4eae3fedc98 100644 --- a/src/Microsoft.EntityFrameworkCore.Commands/tools/about_EntityFramework.help.txt +++ b/src/Microsoft.EntityFrameworkCore.Commands/tools/about_EntityFramework.help.txt @@ -22,6 +22,8 @@ LONG DESCRIPTION -------------------------- --------------------------------------------------- Add-Migration Adds a new migration. + Drop-Database Drops the database. + Remove-Migration Removes the last migration. Scaffold-DbContext Scaffolds a DbContext and entity type classes for a specified database. @@ -34,6 +36,7 @@ LONG DESCRIPTION SEE ALSO Add-Migration + Drop-Database Remove-Migration Scaffold-DbContext Script-Migration diff --git a/src/dotnet-ef/DatabaseCommand.cs b/src/dotnet-ef/DatabaseCommand.cs index a909f888463..f56ff4adbdf 100644 --- a/src/dotnet-ef/DatabaseCommand.cs +++ b/src/dotnet-ef/DatabaseCommand.cs @@ -16,6 +16,7 @@ public static void Configure([NotNull] CommandLineApplication command) command.VerboseOption(); command.Command("update", DatabaseUpdateCommand.Configure); + command.Command("drop", DatabaseDropCommand.Configure); command.OnExecute(() => command.ShowHelp()); } diff --git a/src/dotnet-ef/DatabaseDropCommand.cs b/src/dotnet-ef/DatabaseDropCommand.cs new file mode 100644 index 00000000000..2500f865dcf --- /dev/null +++ b/src/dotnet-ef/DatabaseDropCommand.cs @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using JetBrains.Annotations; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.Extensions.CommandLineUtils; + +namespace Microsoft.EntityFrameworkCore.Commands +{ + public class DatabaseDropCommand + { + public static void Configure([NotNull] CommandLineApplication command) + { + command.Description = "Drop the database for specific environment"; + + var startupProject = command.Option( + "-s|--startup-project ", + "The startup project to use. If omitted, the current project is used."); + var environment = command.Option( + "-e|--environment ", + "The environment to use. If omitted, \"Development\" is used."); + var context = command.Option( + "-c|--context ", + "The DbContext to use. If omitted, the default DbContext is used"); + + var force = command.Option( + "-f|--force", + "Force confirm message. If omitted, confirm message not used"); + command.HelpOption(); + command.VerboseOption(); + + command.OnExecute( + () => Execute(context.Value(), startupProject.Value(), environment.Value(), force.HasValue())); + } + + private static int Execute(string context, string startupProject, string environment, bool isForced) + { + new OperationExecutor(startupProject, environment) + .DropDatabase( + context, + (database, dataSource) => + { + if (isForced) + { + return true; + } + + Reporter.Output.WriteLine( + $"Are you sure you want to drop the database '{database}' on server '{dataSource}'? (y/N)"); + var readedKey = Console.ReadKey().KeyChar; + + return (readedKey == 'y') || (readedKey == 'Y'); + }); + + return 0; + } + } +} diff --git a/src/dotnet-ef/OperationExecutor.cs b/src/dotnet-ef/OperationExecutor.cs index f4b1ed77e4e..177aa6bf106 100644 --- a/src/dotnet-ef/OperationExecutor.cs +++ b/src/dotnet-ef/OperationExecutor.cs @@ -119,6 +119,9 @@ public OperationExecutor([CanBeNull] string startupProject, [CanBeNull] string e rootNamespace)); } + public virtual void DropDatabase([CanBeNull] string contextName, [NotNull] Func confirmCheck) + => _contextOperations.Value.DropDatabase(contextName, confirmCheck); + public virtual MigrationFiles AddMigration( [NotNull] string name, [CanBeNull] string outputDir,