From 33f086d81f6b9066bcd2221760a44de10790bdb7 Mon Sep 17 00:00:00 2001 From: lajones Date: Wed, 18 Mar 2020 18:10:53 -0700 Subject: [PATCH] Partial fix for 8332. Update the ef side to pass in the arguments. --- .../Design/DbContextActivator.cs | 6 ++- .../Internal/AppServiceProviderFactory.cs | 8 ++-- src/EFCore.Design/Design/OperationExecutor.cs | 6 +-- src/ef/Commands/CommandBase.cs | 4 ++ src/ef/Commands/DatabaseDropCommand.cs | 4 +- src/ef/Commands/DatabaseUpdateCommand.cs | 2 +- src/ef/Commands/DbContextInfoCommand.cs | 2 +- src/ef/Commands/DbContextScaffoldCommand.cs | 3 +- src/ef/Commands/DbContextScriptCommand.cs | 2 +- src/ef/Commands/MigrationsAddCommand.cs | 2 +- src/ef/Commands/MigrationsListCommand.cs | 3 +- src/ef/Commands/MigrationsRemoveCommand.cs | 2 +- src/ef/Commands/MigrationsScriptCommand.cs | 3 +- src/ef/IOperationExecutor.cs | 19 ++++---- src/ef/OperationExecutorBase.cs | 46 +++++++++++-------- .../Design/DbContextActivatorTest.cs | 28 +++++++++++ 16 files changed, 93 insertions(+), 47 deletions(-) diff --git a/src/EFCore.Design/Design/DbContextActivator.cs b/src/EFCore.Design/Design/DbContextActivator.cs index e41a7f54439..51141033347 100644 --- a/src/EFCore.Design/Design/DbContextActivator.cs +++ b/src/EFCore.Design/Design/DbContextActivator.cs @@ -22,11 +22,13 @@ public static class DbContextActivator /// The type to instantiate. /// The application's startup assembly. /// The design-time report handler. + /// Arguments passed to the constructor. /// The newly created object. public static DbContext CreateInstance( [NotNull] Type contextType, [CanBeNull] Assembly startupAssembly = null, - [CanBeNull] IOperationReportHandler reportHandler = null) + [CanBeNull] IOperationReportHandler reportHandler = null, + [CanBeNull] string[] arguments = null) { Check.NotNull(contextType, nameof(contextType)); @@ -34,7 +36,7 @@ public static DbContext CreateInstance( new OperationReporter(reportHandler), contextType.Assembly, startupAssembly ?? contextType.Assembly, - args: Array.Empty()) // TODO: Issue #8332 + args: arguments ?? Array.Empty()) .CreateContext(contextType.FullName); } } diff --git a/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs b/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs index 57b9b0621dc..360c325b403 100644 --- a/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs +++ b/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs @@ -44,7 +44,7 @@ public virtual IServiceProvider Create([NotNull] string[] args) _reporter.WriteVerbose(DesignStrings.FindingServiceProvider); return CreateFromHosting(args) - ?? CreateEmptyServiceProvider(); + ?? CreateArgsServiceProvider(args); } private IServiceProvider CreateFromHosting(string[] args) @@ -97,11 +97,13 @@ private IServiceProvider CreateFromHosting(string[] args) } } - private IServiceProvider CreateEmptyServiceProvider() + private IServiceProvider CreateArgsServiceProvider(string[] args) { _reporter.WriteVerbose(DesignStrings.NoServiceProvider); - return new ServiceCollection().BuildServiceProvider(); + return new ServiceCollection() + .AddScoped(typeof(string[]), sp => args) + .BuildServiceProvider(); } } } diff --git a/src/EFCore.Design/Design/OperationExecutor.cs b/src/EFCore.Design/Design/OperationExecutor.cs index 755223fda9f..d9d20ca468b 100644 --- a/src/EFCore.Design/Design/OperationExecutor.cs +++ b/src/EFCore.Design/Design/OperationExecutor.cs @@ -47,6 +47,8 @@ public class OperationExecutor : MarshalByRefObject /// startupTargetName--The assembly name of the startup project. /// projectDir--The target project's root directory. /// rootNamespace--The target project's root namespace. + /// language--The programming language to be used to generate classes. + /// appArgs--Extra arguments passed into the operation. /// /// The . /// The executor arguments. @@ -61,9 +63,7 @@ public OperationExecutor([NotNull] IOperationReportHandler reportHandler, [NotNu _projectDir = (string)args["projectDir"]; _rootNamespace = (string)args["rootNamespace"]; _language = (string)args["language"]; - - // TODO: Flow in from tools (issue #8332) - _designArgs = Array.Empty(); + _designArgs = (string[])args["appArgs"]; var toolsVersion = (string)args["toolsVersion"]; var runtimeVersion = ProductInfo.GetVersion(); diff --git a/src/ef/Commands/CommandBase.cs b/src/ef/Commands/CommandBase.cs index 1bdde78a788..1c708c7352e 100644 --- a/src/ef/Commands/CommandBase.cs +++ b/src/ef/Commands/CommandBase.cs @@ -1,6 +1,7 @@ // 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.Collections.Generic; using Microsoft.DotNet.Cli.CommandLine; using Microsoft.EntityFrameworkCore.Tools.Properties; @@ -8,6 +9,8 @@ namespace Microsoft.EntityFrameworkCore.Tools.Commands { internal abstract class CommandBase { + protected string[] RemainingArguments { get; private set; } + public virtual void Configure(CommandLineApplication command) { var verbose = command.Option("-v|--verbose", Resources.VerboseDescription); @@ -15,6 +18,7 @@ public virtual void Configure(CommandLineApplication command) var prefixOutput = command.Option("--prefix-output", Resources.PrefixDescription); command.HandleResponseFiles = true; + RemainingArguments = command.RemainingArguments.ToArray(); command.OnExecute( () => diff --git a/src/ef/Commands/DatabaseDropCommand.cs b/src/ef/Commands/DatabaseDropCommand.cs index b1b076260de..30f73f5ed7c 100644 --- a/src/ef/Commands/DatabaseDropCommand.cs +++ b/src/ef/Commands/DatabaseDropCommand.cs @@ -15,7 +15,7 @@ protected override int Execute() void LogDropCommand(Func resource) { - var result = executor.GetContextInfo(Context.Value()); + var result = executor.GetContextInfo(Context.Value(), RemainingArguments); var databaseName = result["DatabaseName"] as string; var dataSource = result["DataSource"] as string; Reporter.WriteInformation(resource(databaseName, dataSource)); @@ -38,7 +38,7 @@ void LogDropCommand(Func resource) } } - executor.DropDatabase(Context.Value()); + executor.DropDatabase(Context.Value(), RemainingArguments); return base.Execute(); } diff --git a/src/ef/Commands/DatabaseUpdateCommand.cs b/src/ef/Commands/DatabaseUpdateCommand.cs index ce3ec7bbcd4..79edd47626f 100644 --- a/src/ef/Commands/DatabaseUpdateCommand.cs +++ b/src/ef/Commands/DatabaseUpdateCommand.cs @@ -8,7 +8,7 @@ internal partial class DatabaseUpdateCommand { protected override int Execute() { - CreateExecutor().UpdateDatabase(_migration.Value, _connection.Value(), Context.Value()); + CreateExecutor().UpdateDatabase(_migration.Value, _connection.Value(), Context.Value(), RemainingArguments); return base.Execute(); } diff --git a/src/ef/Commands/DbContextInfoCommand.cs b/src/ef/Commands/DbContextInfoCommand.cs index 4dc81b94350..768c8dac278 100644 --- a/src/ef/Commands/DbContextInfoCommand.cs +++ b/src/ef/Commands/DbContextInfoCommand.cs @@ -11,7 +11,7 @@ internal partial class DbContextInfoCommand { protected override int Execute() { - var result = CreateExecutor().GetContextInfo(Context.Value()); + var result = CreateExecutor().GetContextInfo(Context.Value(), RemainingArguments); if (_json.HasValue()) { diff --git a/src/ef/Commands/DbContextScaffoldCommand.cs b/src/ef/Commands/DbContextScaffoldCommand.cs index 5c6725b56bc..04e7dc2736b 100644 --- a/src/ef/Commands/DbContextScaffoldCommand.cs +++ b/src/ef/Commands/DbContextScaffoldCommand.cs @@ -39,7 +39,8 @@ protected override int Execute() _force.HasValue(), _useDatabaseNames.HasValue(), _namespace.Value(), - _contextNamespace.Value()); + _contextNamespace.Value(), + RemainingArguments); if (_json.HasValue()) { ReportJsonResults(result); diff --git a/src/ef/Commands/DbContextScriptCommand.cs b/src/ef/Commands/DbContextScriptCommand.cs index 6297713bd37..b42ff0f70c7 100644 --- a/src/ef/Commands/DbContextScriptCommand.cs +++ b/src/ef/Commands/DbContextScriptCommand.cs @@ -13,7 +13,7 @@ internal partial class DbContextScriptCommand protected override int Execute() { var sql = CreateExecutor().ScriptDbContext( - Context.Value()); + Context.Value(), RemainingArguments); if (!_output.HasValue()) { diff --git a/src/ef/Commands/MigrationsAddCommand.cs b/src/ef/Commands/MigrationsAddCommand.cs index 1ae2ed839eb..abbe770ea79 100644 --- a/src/ef/Commands/MigrationsAddCommand.cs +++ b/src/ef/Commands/MigrationsAddCommand.cs @@ -22,7 +22,7 @@ protected override void Validate() protected override int Execute() { var files = CreateExecutor().AddMigration( - _name.Value, _outputDir.Value(), Context.Value(), _namespace.Value()); + _name.Value, _outputDir.Value(), Context.Value(), _namespace.Value(), RemainingArguments); if (_json.HasValue()) { diff --git a/src/ef/Commands/MigrationsListCommand.cs b/src/ef/Commands/MigrationsListCommand.cs index dfd775ece7d..21f686550b1 100644 --- a/src/ef/Commands/MigrationsListCommand.cs +++ b/src/ef/Commands/MigrationsListCommand.cs @@ -13,7 +13,8 @@ internal partial class MigrationsListCommand { protected override int Execute() { - var migrations = CreateExecutor().GetMigrations(Context.Value()).ToList(); + var migrations = CreateExecutor() + .GetMigrations(Context.Value(), RemainingArguments).ToList(); if (_json.HasValue()) { diff --git a/src/ef/Commands/MigrationsRemoveCommand.cs b/src/ef/Commands/MigrationsRemoveCommand.cs index 4b8876b7ad4..60944d842fb 100644 --- a/src/ef/Commands/MigrationsRemoveCommand.cs +++ b/src/ef/Commands/MigrationsRemoveCommand.cs @@ -10,7 +10,7 @@ internal partial class MigrationsRemoveCommand { protected override int Execute() { - var result = CreateExecutor().RemoveMigration(Context.Value(), _force.HasValue()); + var result = CreateExecutor().RemoveMigration(Context.Value(), _force.HasValue(), RemainingArguments); if (_json.HasValue()) { ReportJsonResults(result); diff --git a/src/ef/Commands/MigrationsScriptCommand.cs b/src/ef/Commands/MigrationsScriptCommand.cs index bf91dd20b9d..525f5a700ea 100644 --- a/src/ef/Commands/MigrationsScriptCommand.cs +++ b/src/ef/Commands/MigrationsScriptCommand.cs @@ -16,7 +16,8 @@ protected override int Execute() _from.Value, _to.Value, _idempotent.HasValue(), - Context.Value()); + Context.Value(), + RemainingArguments); if (!_output.HasValue()) { diff --git a/src/ef/IOperationExecutor.cs b/src/ef/IOperationExecutor.cs index 73be7242ba2..032180d5090 100644 --- a/src/ef/IOperationExecutor.cs +++ b/src/ef/IOperationExecutor.cs @@ -9,12 +9,12 @@ namespace Microsoft.EntityFrameworkCore.Tools { internal interface IOperationExecutor : IDisposable { - IDictionary AddMigration(string name, string outputDir, string contextType, string migrationNamespace); - IDictionary RemoveMigration(string contextType, bool force); - IEnumerable GetMigrations(string contextType); - void DropDatabase(string contextType); - IDictionary GetContextInfo(string name); - void UpdateDatabase(string migration, string connectionString, string contextType); + IDictionary AddMigration(string name, string outputDir, string contextType, string @namespace, string[] remainingArguments); + IDictionary RemoveMigration(string contextType, bool force, string[] remainingArguments); + IEnumerable GetMigrations(string contextType, string[] remainingArguments); + void DropDatabase(string contextType, string[] remainingArguments); + IDictionary GetContextInfo(string name, string[] remainingArguments); + void UpdateDatabase(string migration, string connectionString, string contextType, string[] remainingArguments); IEnumerable GetContextTypes(); IDictionary ScaffoldContext( @@ -29,10 +29,11 @@ IDictionary ScaffoldContext( bool overwriteFiles, bool useDatabaseNames, string entityNamespace, - string dbContextNamespace); + string dbContextNamespace, + string[] remainingArguments); - string ScriptMigration(string fromMigration, string toMigration, bool idempotent, string contextType); + string ScriptMigration(string fromMigration, string toMigration, bool idempotent, string contextType, string[] remainingArguments); - string ScriptDbContext(string contextType); + string ScriptDbContext(string contextType, string[] remainingArguments); } } diff --git a/src/ef/OperationExecutorBase.cs b/src/ef/OperationExecutorBase.cs index 51b58026bb9..367d0060c04 100644 --- a/src/ef/OperationExecutorBase.cs +++ b/src/ef/OperationExecutorBase.cs @@ -82,45 +82,47 @@ private object InvokeOperationImpl(string operationName, IDictionary arguments) return resultHandler.Result; } - public IDictionary AddMigration(string name, string outputDir, string contextType, string @namespace) + public IDictionary AddMigration(string name, string outputDir, string contextType, string @namespace, string[] remainingArguments) => InvokeOperation( "AddMigration", - new Dictionary + new Dictionary { ["name"] = name, ["outputDir"] = outputDir, ["contextType"] = contextType, - ["namespace"] = @namespace + ["namespace"] = @namespace, + ["appArgs"] = remainingArguments }); - public IDictionary RemoveMigration(string contextType, bool force) + public IDictionary RemoveMigration(string contextType, bool force, string[] remainingArguments) => InvokeOperation( "RemoveMigration", - new Dictionary { ["contextType"] = contextType, ["force"] = force }); + new Dictionary { ["contextType"] = contextType, ["force"] = force, ["appArgs"] = remainingArguments }); - public IEnumerable GetMigrations(string contextType) + public IEnumerable GetMigrations(string contextType, string[] remainingArguments) => InvokeOperation>( "GetMigrations", - new Dictionary { ["contextType"] = contextType }); + new Dictionary { ["contextType"] = contextType, ["appArgs"] = remainingArguments }); - public void DropDatabase(string contextType) + public void DropDatabase(string contextType, string[] remainingArguments) => InvokeOperation( "DropDatabase", - new Dictionary { ["contextType"] = contextType }); + new Dictionary { ["contextType"] = contextType, ["appArgs"] = remainingArguments }); - public IDictionary GetContextInfo(string name) + public IDictionary GetContextInfo(string name, string[] remainingArguments) => InvokeOperation( "GetContextInfo", - new Dictionary { ["contextType"] = name }); + new Dictionary { ["contextType"] = name, ["appArgs"] = remainingArguments }); - public void UpdateDatabase(string migration, string connectionString, string contextType) + public void UpdateDatabase(string migration, string connectionString, string contextType, string[] remainingArguments) => InvokeOperation( "UpdateDatabase", - new Dictionary + new Dictionary { ["targetMigration"] = migration, ["connectionString"] = connectionString, - ["contextType"] = contextType + ["contextType"] = contextType, + ["appArgs"] = remainingArguments }); public IEnumerable GetContextTypes() @@ -138,7 +140,8 @@ public IDictionary ScaffoldContext( bool overwriteFiles, bool useDatabaseNames, string modelNamespace, - string contextNamespace) + string contextNamespace, + string[] remainingArguments) => InvokeOperation( "ScaffoldContext", new Dictionary @@ -154,14 +157,16 @@ public IDictionary ScaffoldContext( ["overwriteFiles"] = overwriteFiles, ["useDatabaseNames"] = useDatabaseNames, ["modelNamespace"] = modelNamespace, - ["contextNamespace"] = contextNamespace + ["contextNamespace"] = contextNamespace, + ["appArgs"] = remainingArguments }); public string ScriptMigration( string fromMigration, string toMigration, bool idempotent, - string contextType) + string contextType, + string[] remainingArguments) => InvokeOperation( "ScriptMigration", new Dictionary @@ -169,12 +174,13 @@ public string ScriptMigration( ["fromMigration"] = fromMigration, ["toMigration"] = toMigration, ["idempotent"] = idempotent, - ["contextType"] = contextType + ["contextType"] = contextType, + ["appArgs"] = remainingArguments }); - public string ScriptDbContext(string contextType) + public string ScriptDbContext(string contextType, string[] remainingArguments) => InvokeOperation( "ScriptDbContext", - new Dictionary { ["contextType"] = contextType }); + new Dictionary { ["contextType"] = contextType, ["appArgs"] = remainingArguments }); } } diff --git a/test/EFCore.Design.Tests/Design/DbContextActivatorTest.cs b/test/EFCore.Design.Tests/Design/DbContextActivatorTest.cs index cc905008f59..0773508db76 100644 --- a/test/EFCore.Design.Tests/Design/DbContextActivatorTest.cs +++ b/test/EFCore.Design.Tests/Design/DbContextActivatorTest.cs @@ -15,6 +15,19 @@ public void CreateInstance_works() Assert.IsType(result); } + [ConditionalFact] + public void CreateInstance_can_pass_in_arguments() + { + var result = DbContextActivator.CreateInstance( + typeof(ArgsContext), arguments: new string[] { "Pass", "Me", "In" }); + + Assert.IsType(result); + Assert.Collection(((ArgsContext)result).Args, + s => { Assert.Equal("Pass", s); }, + s => { Assert.Equal("Me", s); }, + s => { Assert.Equal("In", s); }); + } + private class TestContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder options) @@ -22,5 +35,20 @@ protected override void OnConfiguring(DbContextOptionsBuilder options) .EnableServiceProviderCaching(false) .UseInMemoryDatabase(nameof(DbContextActivatorTest)); } + + private class ArgsContext : DbContext + { + public string[] Args { get; set; } + + public ArgsContext(string[] args) + { + Args = args; + } + + protected override void OnConfiguring(DbContextOptionsBuilder options) + => options + .EnableServiceProviderCaching(false) + .UseInMemoryDatabase(nameof(DbContextActivatorTest)); + } } }