Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create console application for running design-time operations #5949

Merged
merged 2 commits into from
Jul 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions EntityFramework.sln
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.EntityFrameworkCo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.EntityFrameworkCore.Tools.Core.FunctionalTests", "test\Microsoft.EntityFrameworkCore.Tools.Core.FunctionalTests\Microsoft.EntityFrameworkCore.Tools.Core.FunctionalTests.csproj", "{93F36377-39C6-48A2-8C01-FD72373A34A9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tools.Console", "src\Tools.Console\Tools.Console.csproj", "{74A7774B-270E-4D7F-8735-BECB05CACA71}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -204,6 +206,10 @@ Global
{93F36377-39C6-48A2-8C01-FD72373A34A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{93F36377-39C6-48A2-8C01-FD72373A34A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{93F36377-39C6-48A2-8C01-FD72373A34A9}.Release|Any CPU.Build.0 = Release|Any CPU
{74A7774B-270E-4D7F-8735-BECB05CACA71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{74A7774B-270E-4D7F-8735-BECB05CACA71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74A7774B-270E-4D7F-8735-BECB05CACA71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74A7774B-270E-4D7F-8735-BECB05CACA71}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -241,5 +247,6 @@ Global
{D3D0A8E8-EC2F-4E01-8650-8554E186A66F} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
{7583E3F0-8B29-4BEA-A55E-E8B66E6FD508} = {258D5057-81B9-40EC-A872-D21E27452749}
{93F36377-39C6-48A2-8C01-FD72373A34A9} = {258D5057-81B9-40EC-A872-D21E27452749}
{74A7774B-270E-4D7F-8735-BECB05CACA71} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// 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 JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Design
{
public class DatabaseInfo
{
public virtual string DatabaseName { get; [param: NotNull] set; }
public virtual string DataSource { get; [param: NotNull] set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,19 @@ public DbContextOperations([NotNull] ILoggerProvider loggerProvider,
_runtimeServices = startup.ConfigureServices();
}

public virtual void DropDatabase([CanBeNull] string contextType, [NotNull] Func<string, string, bool> confirmCheck)
public virtual void DropDatabase([CanBeNull] string contextType)
{
using (var context = CreateContext(contextType))
{
var connection = context.Database.GetDbConnection();
if (confirmCheck(connection.Database, connection.DataSource))
_logger.Value.LogInformation(DesignCoreStrings.LogDroppingDatabase(connection.Database));
if (context.Database.EnsureDeleted())
{
_logger.Value.LogInformation(DesignCoreStrings.LogDroppingDatabase(connection.Database));
if (context.Database.EnsureDeleted())
{
_logger.Value.LogInformation(DesignCoreStrings.LogDatabaseDropped(connection.Database));
}
else
{
_logger.Value.LogInformation(DesignCoreStrings.LogNotExistDatabase(connection.Database));
}
_logger.Value.LogInformation(DesignCoreStrings.LogDatabaseDropped(connection.Database));
}
else
{
_logger.Value.LogInformation(DesignCoreStrings.Cancelled);
_logger.Value.LogInformation(DesignCoreStrings.LogNotExistDatabase(connection.Database));
}
}
}
Expand Down Expand Up @@ -159,6 +152,19 @@ where i.GetTypeInfo().IsGenericType
return contexts;
}

public DatabaseInfo GetDatabaseInfo([CanBeNull] string contextType)
{
using (var context = CreateContext(contextType))
{
var connection = context.Database.GetDbConnection();
return new DatabaseInfo
{
DatabaseName = connection.Database,
DataSource = connection.DataSource
};
}
}

private Func<DbContext> FindContextFactory(Type contextType)
{
var factoryInterface = typeof(IDbContextFactory<>).MakeGenericType(contextType).GetTypeInfo();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
<Compile Include="Design\Internal\HostingEnvironment.cs" />
<Compile Include="Design\Internal\LoggerProvider.cs" />
<Compile Include="Design\Internal\StartupInvoker.cs" />
<Compile Include="Design\DatabaseInfo.cs" />
<Compile Include="Design\MigrationInfo.cs" />
<Compile Include="Design\MigrationsOperations.cs" />
<Compile Include="Design\OperationException.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,29 @@ private IDictionary AddMigrationImpl(
};
}

public class GetDatabase : OperationBase
{
public GetDatabase([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"];
Execute(() => executor.GetDatabaseImpl(contextType));
}
}

private IDictionary GetDatabaseImpl([CanBeNull] string contextType)
{
var databaseInfo = _contextOperations.Value.GetDatabaseInfo(contextType);
return new Hashtable
{
["DatabaseName"] = databaseInfo.DatabaseName,
["DataSource"] = databaseInfo.DataSource
};
}

public class UpdateDatabase : OperationBase
{
public UpdateDatabase([NotNull] OperationExecutor executor, [NotNull] object resultHandler, [NotNull] IDictionary args)
Expand Down Expand Up @@ -368,14 +391,13 @@ public DropDatabase(
Check.NotNull(args, nameof(args));

var contextType = (string)args["contextType"];
var confirmCheck = (Func<string, string, bool>)args["confirmCheck"];

Execute(() => executor.DropDatabaseImpl(contextType, confirmCheck));
Execute(() => executor.DropDatabaseImpl(contextType));
}
}

private void DropDatabaseImpl(string contextType, Func<string, string, bool> confirmCheck)
=> _contextOperations.Value.DropDatabase(contextType, confirmCheck);
private void DropDatabaseImpl(string contextType)
=> _contextOperations.Value.DropDatabase(contextType);

public abstract partial class OperationBase
{
Expand Down
99 changes: 99 additions & 0 deletions src/Tools.Console/CommandLineOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// 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 Microsoft.Extensions.CommandLineUtils;
using System;

// ReSharper disable ArgumentsStyleLiteral
namespace Microsoft.EntityFrameworkCore.Tools
{
public class CommandLineOptions
{
public ICommand Command { get; set; }
public bool IsHelp { get; set; }
public bool Verbose { get; set; }
public string EnvironmentName { get; set; }
public string Assembly { get; set; }
public string RootNamespace { get; set; }
public string ContentRootPath { get; set; }
public string DispatcherVersion { get; set; }
public string ProjectDirectory { get; set; }
public string DataDirectory { get; set; }
public string StartupAssembly { get; set; }
public string AppConfigFile { get; set; }

public static CommandLineOptions Parse(params string[] args)
{
var options = new CommandLineOptions();

var app = new CommandLineApplication
{
#if NET451
Name = "ef.exe",
#else
Name = "ef.dll",
#endif
FullName = "Entity Framework Core Console Commands"
};

app.HelpOption();
app.VersionOption(Program.GetVersion);
var verbose = app.Option("--verbose", "Show verbose output", inherited: true);

// required
var assembly = app.Option("--assembly <assembly>",
"The assembly file to load.", inherited: true);
#if NET451
var appConfig = app.Option("--config <configfile>",
"The application config file", inherited: true);
#endif

// common options
var startupAssembly = app.Option("--startup-assembly <assembly>",
"The assembly file containing the startup class.", inherited: true);
var dataDirectory = app.Option("--data-dir <dir>",
"The folder used as the data directory (defaults to current working directory).", inherited: true);
var projectDirectory = app.Option("--project-dir <dir>",
"The folder used as the project directory (defaults to current working directory).", inherited: true);
var contentRootPath = app.Option("--content-root-path <dir>",
"The folder used as the content root path for the application (defaults to application base directory).", inherited: true);
var rootNamespace = app.Option("--root-namespace <namespace>",
"The root namespace of the target project (defaults to the project assembly name).", inherited: true);
var environment = app.Option(
"-e|--environment <environment>",
"The environment to use. If omitted, \"Development\" is used.", inherited: true);

// inherited
var dispatcherVersion = app.Option("--dispatcher-version <version>",
"The dispatcher version", inherited: true);
dispatcherVersion.ShowInHelpText = false;

EfCommand.Configure(app, options);

var result = app.Execute(args);

if (result != 0)
{
return null;
}

options.IsHelp = app.IsShowingInformation;

options.Verbose = verbose.HasValue();
options.DispatcherVersion = dispatcherVersion.Value();

options.Assembly = assembly.Value();
options.StartupAssembly = startupAssembly.Value();
options.DataDirectory = dataDirectory.Value();
options.ProjectDirectory = projectDirectory.Value();
options.ContentRootPath = rootNamespace.Value();
options.RootNamespace = contentRootPath.Value();
options.EnvironmentName = environment.Value();
#if NET451
options.AppConfigFile = appConfig.Value();
#endif

return options;
}
}
}
25 changes: 25 additions & 0 deletions src/Tools.Console/Commands/DatabaseCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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 Microsoft.Extensions.CommandLineUtils;

namespace Microsoft.EntityFrameworkCore.Tools
{
public class DatabaseCommand
{
public static void Configure(CommandLineApplication command, CommandLineOptions options)
{
command.Description = "Commands to manage your database";
command.HelpOption();

command.Command("update", c => DatabaseUpdateCommand.Configure(c, options));
command.Command("drop", c => DatabaseDropCommand.Configure(c, options));

command.OnExecute(() =>
{
EfCommand.WriteLogo();
command.ShowHelp();
});
}
}
}
62 changes: 62 additions & 0 deletions src/Tools.Console/Commands/DatabaseDropCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.Extensions.CommandLineUtils;
using Microsoft.EntityFrameworkCore.Tools.Internal;

namespace Microsoft.EntityFrameworkCore.Tools
{
public class DatabaseDropCommand : ICommand
{
public static void Configure([NotNull] CommandLineApplication command, [NotNull] CommandLineOptions commonOptions)
{
command.Description = "Drop the database for specific environment";
command.HelpOption();

var context = command.Option(
"-c|--context <context>",
"The DbContext to use. If omitted, the default DbContext is used");
var force = command.Option(
"-f|--force",
"Drop without confirmation");

command.OnExecute(() => { commonOptions.Command = new DatabaseDropCommand(context.Value(), force.HasValue()); });
}

private readonly bool _force;
private readonly string _context;

public DatabaseDropCommand(string context, bool force)
{
_context = context;
_force = force;
}

public void Run(IOperationExecutor executor)
{
if (!_force)
{
var result = executor.GetDatabase(_context);
if (result == null)
{
Reporter.Output("Could not find database to drop");
return;
}

Reporter.Output(
$"Are you sure you want to drop the database '{result["DatabaseName"]}' on server '{result["DataSource"]}'? (y/N)");
var readedKey = Console.ReadKey().KeyChar;
var confirmed = (readedKey == 'y') || (readedKey == 'Y');
if (!confirmed)
{
Reporter.Output("Cancelled");
return;
}
}

executor.DropDatabase(_context);
}
}
}
40 changes: 40 additions & 0 deletions src/Tools.Console/Commands/DatabaseUpdateCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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 JetBrains.Annotations;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.EntityFrameworkCore.Tools.Internal;

namespace Microsoft.EntityFrameworkCore.Tools
{
public class DatabaseUpdateCommand : ICommand
{
public static void Configure([NotNull] CommandLineApplication command, [NotNull] CommandLineOptions commonOptions)
{
command.Description = "Updates the database to a specified migration";
command.HelpOption();

var migration = command.Argument(
"[migration]",
"The target migration. If '0', all migrations will be reverted. If omitted, all pending migrations will be applied");

var context = command.Option(
"-c|--context <context>",
"The DbContext to use. If omitted, the default DbContext is used");

command.OnExecute(() => { commonOptions.Command = new DatabaseUpdateCommand(migration.Value, context.Value()); });
}

private readonly string _targetMigration;
private readonly string _contextType;

public DatabaseUpdateCommand(string targetMigration, string contextType)
{
_targetMigration = targetMigration;
_contextType = contextType;
}

public void Run(IOperationExecutor executor)
=> executor.UpdateDatabase(_targetMigration, _contextType);
}
}
Loading