Efcore: Tools: Flow arguments into IDesignTimeDbContextFactory

Created on 29 Apr 2017  ·  74Comments  ·  Source: dotnet/efcore

We've enabled the ability for arguments to be passed into the Create method (via a string[] args parameter), but we need to think through the design of how they get specified on the command line.

For dotnet ef and ef, I think it's obvious: any unknown argument will be passed verbatim.

dotnet ef database update --connection-string "Data Source=prod.db"

It gets a little more complicated on PowerShell where the paradigm is so different. Right now, the easiest way I can think of is to use a string array parameter.

Update-Database -AppArgs '--connection-string', 'Data Source=prod.db'
area-migrations area-tools closed-fixed punted-for-2.0 type-enhancement

Most helpful comment

Sounds like most people here are really just waiting for issue #10750

All 74 comments

Another harebrained idea I had was to apply a convention to the unknown arguments. For example, -ConnectionString would magically be translated to --connection-string, but this falls down if someone tries to take advantage of PowerShell's case-insensitivity -connectionstring or ability to under-specify -Conn.

@bricelam would something like this work for PowerShell?

Update-Database -CommandLineArgs "--connection-string 'Data Source=prod.db'"

Ideally I want to be able to copy and paste command line arguments I use for invoking the application into one argument of the PowerShell command and only make minimal edits.

Yep. That would work too.

(assuming you swapped the apostrophes and quotes)

You'd also need to handle argument quoting and escaping yourself. Whereas with the array, we could handle that for you.

Need this now :) Kind of relates directly to my question on SO: https://stackoverflow.com/questions/46583136/how-to-run-dotnet-ef-commands-to-target-a-specific-appsettings-environment-j#comment80159150_46583136

@bricelam , can I pass arguments and have this work with the current released version?

No, this has not been implemented. You could read environment variables, a file, etc. inside IDesignTimeDbContextFactory.

Note to implementer, we should allow them to be specified with DbContextActivator.CreateInstance() too.

Let me give you a use case that I think highlights why tackling this issue is important. When developing databases, I typically have a "development" database that my code-in-development uses. I also have a complementary "test" database that is used by my integration tests. This is separate from the "development" database so as not to interfere with that atomic nature of the integration tests (I run all tests in an ambient transaction, ensuring any database changes are rolled back after each test - that's another issue). In the past, I've used Fluent Migrations to keep all of my databases in sync (dev, test, staging, production) and was able to execute the database update by passing in an argument that specifies the connection string to pull from the app.config file in the migration project. Now that I'm trying to use EF Core 2.0 and code-first migrations, I can't find a way to specify the connection string to use when executing "dotnet ef database update". I created an implementation of the IDesignTimeDbContextFactory interface, but the args in the constructor are not used. Would be nice to be able to pass in the argument in the "update-database" command to indicate the connection string.

For my use case i need to specify the connection string name to the command.

Do we have an ETA on when it will be implemented?

@dkehring @Korigoth The typical way to do this is to read a file, an environment variable, or similar inside IDesignTimeDbContextFactory. Does this work for you? If not, can you explain a bit more why?

@ajcvickers i did it with an environment variable for now and it's working as expected, so i've created different batch file for all the jobs we need.

but thanks for the reply, may be it's not documented enough on the document websites?

@ajcvickers I need to be able to target a specific database on the command line. Reading from a file or environment variable won't work. Let's say I have a file that contains the connection strings for the dev and test databases. If I read from this file, how do I select the connection string I need? What I'd like to be able to do is something like this:

C:> dotnet ef database update "test"
... or ...
C:> dotnet ef database update "dev"

Then in my implementation of IDesignTimeDbContextFactory, I can read a local config file and use the argument string passed in ("test", "dev", etc.) to select the correct connection string based on that key. I need something in IDesignTimeDbContextFactory that can affect the logic of the connection string selection. Hope that clarifies. I'm not sure what you're doing in your suggestion about environment variables. Does your batch job change the environment variable before the database update is executed?

@dkehring Exactly, i do set ConnStringName=YourConnectionStringNameNeededForTheJob then i do dotnet ef database update or anything required. (in cmd)

Here is the piece of code i use:

    public EspaceBiereContext CreateDbContext(string[] args)
    {
        var appSettingsFinder = new DefaultAppSettingsFinder();
        var path = appSettingsFinder.FindPath<DbContextFactory>("EspaceBiere.sln", "EspaceBiere.Web");
        var config = new ConfigBuilder()
            .SetBasePath(path)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();

        var connectionStringName = GetEnvironmentVariable("ConnName", ConnNameError);
        var connectionString = config.GetConnectionString(connectionStringName);
        var builder = new DbContextOptionsBuilder<EspaceBiereContext>();
        builder.UseSqlServer(connectionString);

        return new EspaceBiereContext(builder.Options);
    }

    private string GetEnvironmentVariable(string name, string errorMessage)
    {
        var connectionStringName = Environment.GetEnvironmentVariable(name);

        if (string.IsNullOrWhiteSpace(connectionStringName))
        {
            throw new Exception(errorMessage);
        }

        return connectionStringName;
    }

@ajcvickers Thanks for sharing. This is an interesting hack, but hardly an optimal solution. In my opinion, environment variables define the local machine environment and are - for the most part - fairly static. Sort of like the old machine.config file. Your solution works, but it's a work-around due to an incomplete implementation of IDesignTimeDbContextFactory. In fact, it's interesting that the input paramater "args" on IDesignTimeDbContextFactory.CreateDbContext is marked with the NotNull attribute.

image

@ajcvickers Also, is your solution cross-platform?

@dkehring i dont know if its cross-platform and i know it's kinda hacky, but until dotnet ef is fully ready, we found that way to make it work. I know that u can set EnvironmentVariable on CMD, Linux, PowerShell. But they all have different syntax. I only know the syntax for CMD.

@ajcvickers No framework is ever fully ready, evolving over time as driven by new and ever-changing needs. I would ask that this issue be re-opened and re-examined for consideration. The pretenses under which it was closed are not clear (@bricelam), and given the "args" parameter is there, but not implemented, it seems reasonable to ask for the implementation to be completed.

Regarding your solution, since .NET/EF Core are touted as cross-platform, forcing developers to implement non-cross-platform solutions should be avoided. Regardless, the use of environment variables on any platform in this way is, in my opinion, a misuse.

I would ask that this issue be re-opened and re-examined for consideration.

This issue is open, and considered when planning releases.

@bricelam Indeed. I was swayed by the issues closed on 10/6/17. Thanks.

@dkehring @Korigoth Thanks for the additional information.

The suggested solution of being able to pass arguments to the IDesignTimeDbContextFactory would be great for my use case, a multi-tenant application where each tenant has their own database.

Right now I'm using the workaround of setting an env-var with the connection string.

What is the purpose of the args parameter, I can't see a way to populate it?

I am in a similar situation as @dkehring and would also love to see support for command line arguments to be passed to the db context factory.

I am using a generic host with Entity Framework so I am forced to use IDesignTimeDbContextFactory for dotnet ef commands, it also is perplexing that I had to spend 10 minutes googling and trying a bunch of dotnet ef commands only to find out i can never get any arguments over to CreateDbContext(string[] args). I think this is trivial and should be fixed ASAP.

Suggested command usage would be:
dotnet ef migrations add InitialMigration -- arg1 arg2 etc

I think this is trivial and should be fixed ASAP.

We'd be happy to work with you if you're interested in contributing this feature. There are a few things to consider in the design:

  • What does the syntax look like in PowerShell?
  • Do these arguments override or augment launchSettings.json? (See #8695)
  • How do you differentiate between these arguments and arguments sent to the provider? (See #11982)

@bricelam I'd be willing to work on this.

Awesome, if you can come up with proposal, I can run it by the team in a design meeting. Once we agree on a design, we can start iterating on the implementation in a PR.

Sorry for calling it trivial, I realize there is a lot that goes into a fix like this. I guess I should have said that the scope of the issue seems trivial. IE just pass us extra args from the CLI. As adoption for the GenericHost-like setup grows more people will want a web host style experience which include migrations, config files, DI, CLI arguments, environment variables.

It's also worth noting that the WebHost pattern works with generic host: (but yeah, still no ability to specify args)

class Program
{
    static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateWebHostBuilder(string[] args)
        => new HostBuilder()
            .ConfigureServices(
                services => services
                    .AddDbContext<MyDbContext>());
}

It's a little strange that you have to call it CreateWebHostBuilder, but 🤷‍♂️ hindsight's 20/20

Any update on this? when can we change the connection string withdotnet ef database update

We could enable that specifically via a parameter like --connection after #8494 is implemented.

Hi. Any news on this? I have exactly the same scenario as @MatthewLymer . I just wanted to check in, before actually starting on some workaround, or should I wait ?

Thanks.

@sekulicb Thanks for your interest. Unfortunately, this issue is in the "Backlog" milestone. This means that while we do intend to implement this in the future it is not currently scheduled for a release. You're probably going to be better off pursuing a workaround.

Hi @ajcvickers . Thank you for quick reply. Currently my implementation is "reading from file", as suggested previously. But anyway, looking forward to more permanent solution from you guys. All the best.

I am also looking forward for having this feature released. However;

Flow arguments into IDesignTimeDbContextFactory

Why just IDesignTimeDbContextFactory? Isn't it better the way you previously described it as:

Note to implementer, we should allow them to be specified with DbContextActivator.CreateInstance() too.

@CoskunSunali That gets messy since IDesignTimeDbContextFactory is an interface, and some people won't need/want the args. But we haven't discussed the design as a team, so we're open to proposals.

Scratch that, I misread the question.

DbContextActivator.CreateInstance() is an API for tool writers that eventually calls IDesignTimeDbContextFactory.CreateDbContext().

I'm not sure that IDesignTimeDbContextFactory is the right abstraction... Yes, we want to create a DbContext, but we also need to gather all the information about the environment and relevant application configuration and settings in order to do this.

The ideal solution would be to introduce dependency injection and use the generic host functionality.

Instead of IDesignTimeDbContextFactory we could have a IDesignTimeHostBuilderFactory class which would be used to create an IHostBuilder similar to what you see with the CreateWebHostBuilder function in the Program.cs file of an an ASP.NET Core app.

When running Add-Migration or Update-Database, or the dotnet equivalents, an instance of an IDesignTimeHostBuilderFactory is created and the CreateHostBuilder method is called with the args passed to it. This method returns an IHostBuilder object which contains all the configuration set up required to create an instance of our database context. The migration functions could then request our database context via the inherent dependency injection functionality.

As a side note, it would be nice to see HostBuilder be modified to have a similar signature to current ASP.NET Core apps with the ultimate goal of accepting a Startup class where we can set up the database configuration for the migration process. That is HostBuilder.CreateDefaultBuilder(args).UseStartup<Startup>().

The code below demonstrates a complete example...

```C#
namespace MyProject
{
public class MyHostBuilderFactory : IDesignTimeHostBuilderFactory
{
public IHostBuilder CreateHostBuilder(string[] args) =>
Microsoft.Extensions.Hosting.HostBuilder
.CreateDefaultBuilder(args)
.UseStartup()
;
}

public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.AddConfiguration(configuration)
.AddYamlFile("appsettings.yaml", optional: true, reloadOnChange: true)
.AddYamlFile($"appsettings.{env.EnvironmentName.ToLower()}.yaml", optional: true, reloadOnChange: true)
;

  Configuration = builder.Build();
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
  services.AddDbContext<MyDbContext>(options => {
    var connectionString = Configuration.GetConnectionString("DefaultConnection"));
    var builder = new System.Data.SqlClient.SqlConnectionStringBuilder(connectionString) {
      Password = Configuration["DbPassword"]
    };
    options.UseSqlServer(builder.ConnectionString);
  });
}

}

public class MyDbContext : Microsoft.EntityFrameworkCore.DbContext
{
public MyDbContext(DbContextOptions options) : base(options) { }
}
}
```

Note, we detect and use the generic host pattern (Program.CreateHostBuilder) at design time in 3.0.

You only need IDesignTimeDbContextFactory if you want to configure your services differently between run time and design time.

I do handle it this way:

// Create DB context with connection from your AppSettings 
var optionsBuilder = new DbContextOptionsBuilder<AppContext>();
if(appSettings.UseInMemory) {
optionsBuilder = appSettings.UseInMemory
   ? optionsBuilder.UseInMemoryDatabase("MyInMemoryDB")
   : optionsBuilder.UseMySql(appSettings.BuildConnectionString());

return new AppContext(optionsBuilder.Options);

For more details see my SO answer here: https://stackoverflow.com/a/53924899/804385

You only need IDesignTimeDbContextFactory if you want to configure your services differently between run time and design time.

Unless you actually want the args!? :-)

I'd personally like to see something like this:

        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();         
        }

        [EfCoreDesignTime]
        private static void MainDesignTime(string[] args)
        {
                    CreateHostBuilder(args, true).Build().Run();         
        }

        public static IHostBuilder CreateHostBuilder(string[] args, bool designTime = false)
        {               
                 // if designTime is true - you might register dbcontext differently here - you could even directly construct: new MyDesignTimeDbContextFactory(args) and register it as singleton.
        }

@dazinator You can already use command line arguments in the host builder using the command-line configuration provider.

@DanVicarel perhaps you misunderstood.. I am aware you can use command line configuration provider. This issue is that the dotnet ef tool does not allow you to pass arbitrary arguments in via the command line when using dotnet EF commands. Therefore adding command line configuration provider gets you nowhere - dot ef commands dont pass them in at design time. Please read above thread for full context of this issue and hopefully that will then make sense to you.

I agree (partialy) with @dazinator, it would be really nice to be able to init the design time-stuff with a host builder as you would with pretty much everything else these days in dotnet core. I don't really see any point for the EfCoreDesignTime-attribute though, I prefer to have everything concerning design time in a separate project.

EDIT: I don't really see any point for different entry points in the same Program.cs

Why can't it just look at the ASPNETCORE_ENVIRONMENT variable and default to "Development" if one isn't found like the rest of .net's config does... then it can get the correct connection string from the relevant json file.

appsettings.json
appsettings.Development.json
etc.

right now there is no way to run database update against the development database/connection string without manually copy/pasting it into the production appsettings.json file... which is just silly for a product that's supposed to support CI/CD.

I faced the same problem, I'm often should update several contexts. It's not very comfortable to change a connection string for migration every time when you want to update a database, especially when you have several appsettings files and several environments. In an ideal case, I don't want keep connection string for migrations in the appsettings file because I don't need a user with so permissions at runtime. So currently my workaround is read connection string from console inside the CreateDbContext method + simple bat file.

public class MyContextFactory : IDesignTimeDbContextFactory<MyContext>
{
        public MyContext CreateDbContext(string[] args)
        {
            var builder = new DbContextOptionsBuilder<MyContext>();

            var connectionString = Console.ReadLine();
            builder.UseSqlServer(connectionString);

            return new MyContext(builder.Options);
        }
}
set connectionString=myConnectionString
echo %connectionString% | dotnet ef database update

I'm not sure it will be work in all cases but it works for me and maybe will be helpful for someone else

Sounds like most people here are really just waiting for issue #10750

It seems args is just a vestige - i would recommend removing the parameter if they will always be an empty array and will never contain any args.

Dont know why they are marked with NotNull.

Re-opening to discuss making it easier to understand what is happening when unknown arguments are used. Previously, if you mistyped an argument, then the command line would output a nice message saying it was not recognized. Now it just silently passes the argument to the factory, which in many cases will mean silent incorrect behavior in some way.

Two ideas:

  • Clearly output on the command line which arguments are not recognized and are being passed through
  • Only pass through unknown arguments if switched on with another option--e.g. --flow-args

I have no problem with the first idea. Note: if you turn on --verbose you can already see what the RemainingArguments are.

Another, slightly different, 3rd idea is: Currently we pass in everything we find that we don't understand as an arg. We could instead change it so there's a flag --args (which would have to be at the end of the list of other args) which interprets everything after it as args to be passed. I.e. you'd have to explicitly elect to do this. I'd have to investigate if that plays well with Powershell though - where arg order is harder to know.

Also not sure if this would play well with PowerShell, but instead of an --args flag you could just use --. I think that's a pretty familiar pattern to shell users that says "everything else is an argument".

Some notes about what the code does right now and possible changes to accommodate the above.

Command-Line
There is a flag in the command-line code called AllowArgumentSeparator which supposedly allows commands to deal with --. There is a comment in the code which says:
```C#
// a stand-alone "--" is the argument separator, so skip it and
// handle the rest of the args as unexpected args

However, the code has bugs:
1. the code fails to recognize the `AllowArgumentSeparator` on the correct command - and so at the moment that code gets skipped,
2. even with that fixed, the existing code also does not "handle the rest of the args as unexpected args", instead it calls `HandleUnexpectedArg()` on the next argument (without checking that it exists), but then goes back to parsing all other arguments/options as usual.

What we would want to do to achieve the above is to revert the original change which set all commands `_throwOnUnexpectedArg` to false always and instead only set any particular command's `_throwOnUnexpectedArg` to false when we encounter `--` (`_throwOnUnexpectedArg` would need a new setter), and then actually parse _all_ command-line args after that as unexpected args (which will add them to `RemainingArgs` which is what we want). This is all possible but means we move further away from the existing parser code.

It would also be possible to do the same thing with a different flag (e.g. `--args`) of course, but `--` is recognized for this purpose.

**Powershell**
Unfortunately our Powershell CmdLets do not suit themselves to the purpose of "everything after this is interpreted as an argument to pass on".

`PositionalBinding` of parameters is `false` for all our CmdLets, and it would be a breaking change to turn it on - so the parameters can appear in any order, possibly interspersed with unknown arguments, and the only way of getting those unknown arguments is to use `[ValueFromRemainingArguments]` which is what we do now.

It would be possible to scan the `ValueFromRemainingArguments` parameter for e.g. `--` and only pass into the application any arguments after that. But that doesn't mean that _everything_ after the `--` _on the command-line_ would be passed on - only those parameters which the CmdLet had not otherwise interpreted - so e.g. passing the string `-Connection` as one of the arguments might be difficult because `Scaffold-DbContext` would it interpret it as one of the other parameters (it might be possible to get around this by quoting - but then you would have to know to do that).

Another alternative is remove the `[ValueFromRemainingArguments]` parameter altogether and instead define a known parameter, say `-Arguments`, of type `string[]`, but this means you would need to use the CmdLet by passing a comma-separated list like this:

Scaffold-DbContext -Connection xyz -Provider abc -Arguments FirstArg,SecondArg,ThirdArg these interpreted separately from Arguments
```

Anything after ThirdArg would be interpreted as normal arguments to Scaffold-DbContext. Also if what you wanted to pass was something which Scaffold-DbContext could interpret as a different parameter that might cause problems (possibly solvable by quoting as above). Similarly trying to pass anything containing a comma (unfortunately, a SQL Server connection string would come under this heading).

If we went this latter route, that might argue for using --args for the Command-Line approach above, in order to be consistent between Powershell and Command-Line.

Re the CLI tool, as @bricelam said in design, -- really seems like the correct, idiomatic solution for this. I'm not sure who's doing the command-line parser here (is it us?), but if there are issues/bugs there, we'd definitely ideally fix them rather than change our design (e.g. use --args).

Re Powershell:

It would be possible to scan the ValueFromRemainingArguments parameter for e.g. -- and only pass into the application any arguments after that. But that doesn't mean that everything after the -- on the command-line would be passed on - only those parameters which the CmdLet had not otherwise interpreted - so e.g. passing the string -Connection as one of the arguments might be difficult because Scaffold-DbContext would it interpret it as one of the other parameters (it might be possible to get around this by quoting - but then you would have to know to do that).

Just to be sure I understand: you're saying that a -Connection appearing after the -- would still be interpreted by Scaffold-DbContext, right? If so, that's indeed unfortunate but it doesn't seem that problematic to me - the user would be responsible for ensuring their switches don't conflict with ours, that seems like a reasonable limitation to me... However, we'd still throw if an unknown switch was specified before to --, right?

One note... If we end up having an explicit argument for flowthrough arguments (i.e. --args, -Arguments), it may be to allow multiple occurrences of that, rather than comma-concatenation which raises questions of escaping etc. I'd also give it So rather than:

Scaffold-DbContext -Connection xyz -Provider abc -Arguments FirstArg,SecondArg,ThirdArg these interpreted separately from Arguments
Scaffold-DbContext -Connection xyz -Provider abc -ExtraArgument FirstArg -ExtraArgument SecondArg -ExtraArgument ThirdArg these interpreted separately from Arguments

(also propose renaming -Argument to -ExtraArgument because argument is extremely generic.

The last recommendation is possible Update: I just checked and I take it back - it does not appear that the last recommendation is even possible in Powershell. You get an error:

Cannot bind parameter because parameter 'Xyz' is specified more than once. To provide multiple values to 
parameters that can accept multiple values, use the array syntax. For example, "-parameter value1,value2,value3"

Also it would be inconsistent with what we do now. E.g. on the command-line you specify multiple tables by doing

dotnet ef dbcontext scaffold ... -table Schema1.Table1 -table Schema1.Table2 ...

but in PMC you do:

Scaffold-DbContext ... -Tables Schema1.Table1,Schema1.Table2 ...

So I'd recommend we do the same for -Arguments (or whatever we end up calling it).

However, we'd still throw if an unknown switch was specified before to --, right?

Since we're in PMC we wouldn't throw there - but we could print an error message and stop.

you're saying that a -Connection appearing after the -- would still be interpreted by Scaffold-DbContext, right?

Right. Basically if you pass in a mixture of known and unknown args/options, the known ones will be assigned by Powershell to the matching parameter, the unknown ones, wherever they were on the command-line, will be captured using a [ValueFromRemainingArguments]-parameter which is a string[]. We can then look at that string[] and pass on whatever we feel is appropriate, or error.

Decisions from Design Meeting 6/10/2020.

Command-Line
We will use update the command-line code to use -- and fix the bugs as outlined above.

Powershell
We will use the -Arguments approach outlined above except it will be called -Args and will take a single string parameter - which means multiple arguments would need to be quoted:

Scaffold-DbContext -Connection xyz -Provider abc -Args 'FirstArg SecondArg ThirdArg'

For Powershell, the CmdLet just collects all the arguments and calls the command-line equivalent. Whatever is in the string argument to -Args will be passed in as the argument of the -- command-line option without interpretation.

The approach above was checked in on June 16, 2020.

For the purposes of the explanation below "normal arguments" are arguments or options that a particular command understands, which influence the way that command does its work, e.g. the name of the migration to create when calling migrations add or Add-Migration is a "normal argument"; so is --output-dir or -OutputDir.

"Application arguments", on the other hand, are any arguments which the command should pass on to whatever application is involved (usually one that creates and/or uses a DbContext class referred to by the command).

Command-Line
The commands will no longer interpret any extraneous arguments as application arguments. Instead, if you want to call a dotnet ef command with application arguments, you must now put -- after you pass in all the normal arguments, followed by whatever you want to the application arguments to be. All arguments after the -- will be interpreted as application arguments. If you need to pass in an application argument which, for instance, contains a space you will need to quote that argument e.g.

dotnet ef migrations add InitialCreate -- FirstAppArg "This is all the second application argument" ThirdAppArg

Powershell
Similarly the Powershell commands will no longer interpret any extraneous arguments as arguments to be passed to the application. Instead you need to add a -Args argument to the command. This will take a single string parameter which can represent as many application arguments as you wish.

The single string parameter means a couple of differences compared to the command-line.

  1. If you wish to pass multiple application arguments you must quote the whole set of application arguments so that it looks like a single string argument to Powershell (see here for a description of quoting in Powershell). E.g.:
Add-Migration InitialCreate -Args 'FirstAppArg SecondAppArg'
  1. Once Powershell has interpreted the quotes (if any), the single string argument is appended (after --) to the underlying dotnet ef command including any remaining quotes. At that point the single string will be interpreted as if you had passed it in on the command-line, so if you e.g. want to pass in an argument containing spaces you will need to ensure that what is passed to the dotnet ef command contains the second set of quotes necessary for it to understand that the argument containing the space is one argument. E.g.:
Add-Migration InitialCreate -Args 'FirstAppArg "This is all the second application argument" ThirdAppArg'
  1. In distinction to the dotnet ef command, passing further arguments after the single string argument which -Args uses will cause an error: E.g. this will cause an error:
Add-Migration InitialCreate -Args FirstAppArg MistakenAppArg

because the Add-Migration command interprets FirstAppArg as _all_ of the application arguments and will try to interpret MistakenAppArg as a normal argument. This will fail because Add-Migration has no MistakenAppArg argument.

@lajones Have these changes been deployed yet? I've just updated to 3.1.5 and the following command dotnet ef database update -- production is producing the following error:

Build started...
Build succeeded.
Specify --help for a list of available options and commands.
Unrecognized option '--'

For completeness sake, I am using a context factory and was hoping to swap out connection strings based on the command line args.

@HugoPeters1024 I'm sorry but these changes were checked in to the 5.0 release rather than the 3.1 release, specifically they will be available in 5.0 preview 7.

@lajones Oh that's my bad. Not sure how but I somehow failed to see that. I'll be looking forward to it then, cheers ;)

@HugoPeters1024 No problem. When it is available let us know if there are any problems. Cheers.

I can pass -AppArgs parameters with Add-Migration command. It Works. Nice.
Add-Migration InitialCreate -Context MasterDbContext -OutputDir Migrations\Master -AppArgs 'P1' 'P2'

I also check it, with this -Migration parameter it works.
Update-Database -Migration 20200718203431_InitialCreate -Context MasterDbContext -AppArgs 'P1' 'P2'

But I can't pass with Update-Database command.
Update-Database -Context MasterDbContext -AppArgs 'P1' 'P2'

I don't see all args parameters.
Have you any suggestion ?

@ismailkocacan I tried this with the preview 7 tools. The syntax to use is: -Args P1 or if passing multiple args, then -Args 'P1 P2' . This worked for me for both Add-Migration and Update-Database commands.

Can you check that you have the preview 7 Tools package installed?

@ismailkocacan I tried this with the preview 7 tools. The syntax to use is: -Args P1 or if passing multiple args, then -Args 'P1 P2' . This worked for me for both Add-Migration and Update-Database commands.

Can you check that you have the preview 7 Tools package installed?

@ajcvickers Yes, I checked. It works. Thanks.

I installed dotnet ef 5.0.0-preview.7.20365.15.
How can I specifiy args parameters with dotnet ef ?

I execute below command and I don't see -args parameters ?

dotnet ef database update --help

Usage: dotnet ef database update [arguments] [options]

Arguments:
  <MIGRATION>  The target migration. If '0', all migrations will be reverted. Defaults to the last migration.

Options:
  --connection <CONNECTION>              The connection string to the database. Defaults to the one specified in AddDbContext or OnConfiguring.
  -c|--context <DBCONTEXT>               The DbContext to use.
  -p|--project <PROJECT>                 The project to use.
  -s|--startup-project <PROJECT>         The startup project to use.
  --framework <FRAMEWORK>                The target framework.
  --configuration <CONFIGURATION>        The configuration to use.
  --runtime <RUNTIME_IDENTIFIER>         The runtime to use.
  --msbuildprojectextensionspath <PATH>  The MSBuild project extensions path. Defaults to "obj".
  --no-build                             Don't build the project. Only use this when the build is up-to-date.
  -h|--help                              Show help information
  -v|--verbose                           Show verbose output.
  --no-color                             Don't colorize output.
  --prefix-output                        Prefix output with level.

@ismailkocacan From the comment above, use this syntax:

dotnet ef migrations add InitialCreate -- FirstAppArg "This is all the second application argument" ThirdAppArg

@ismailkocacan From the comment above, use this syntax:

dotnet ef migrations add InitialCreate -- FirstAppArg "This is all the second application argument" ThirdAppArg

@ajcvickers Did you read my ask ? I don't want to migrations add with dotnet ef.

It works with Package Manager Console
Update-Database -Migration MigrationName -Context MasterDbContext -Args 'P1 P2'

How can I specifiy args parameters with dotnet ef when I update database ?

@ismailkocacan It's the same syntax:

dotnet ef database update -- FirstAppArg "This is all the second application argument" ThirdAppArg

@ismailkocacan It's the same syntax:

dotnet ef database update -- FirstAppArg "This is all the second application argument" ThirdAppArg

EF Core cli doesn't seem to be working on version 5.0.0-preview.8.20407.4.

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
    public MyDbContext CreateDbContext(string[] args)
    {
        var connectionString = args[0];
        return new MyDbContext(new DbContextOptionsBuilder<MyDbContext>().UseSqlServer(connectionString).Options);
    }
}
C:\Temp\MyApp>dotnet ef database update -- FirstAppArg "This is all the second application argument" ThirdAppArg
Build started...
Build succeeded.
System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at MyApp.DesignTimeDbContextFactory.CreateDbContext(String[] args) in C:\Temp\MyApp\MyApp\DesignTimeDbContextFactory.cs:line 19
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContextFromFactory(Type factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.<>c__DisplayClass13_1.<FindContextTypes>b__9()
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)

@macorx What version of the ef tool are you using? (Found by running dotnet ef --info.)

@ajcvickers

C:\repos\MyApp>dotnet ef --info

...

Entity Framework Core .NET Command-line Tools 5.0.0-preview.8.20407.4

@macorx I am not able to reproduce this. Please open a new issue and attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

@ajcvickers I found out what happened. I wrongly assumed I had to update only ef tool to version 5.0.0-preview.8.20407.4.
You must also update the project's package references to this version as well.

After I have done that, it has worked as expected.

Was this page helpful?
0 / 5 - 0 ratings