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

Tools: Flow arguments into IDesignTimeDbContextFactory #8332

Open
bricelam opened this Issue Apr 29, 2017 · 34 comments

Comments

Projects
None yet
@bricelam
Member

bricelam commented Apr 29, 2017

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 -ContextArgs '--connection-string', 'Data Source=prod.db'
@bricelam

This comment has been minimized.

Member

bricelam commented Apr 29, 2017

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.

@ajcvickers ajcvickers added this to the 2.0.0 milestone May 1, 2017

@divega

This comment has been minimized.

Member

divega commented May 1, 2017

@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.

@bricelam

This comment has been minimized.

Member

bricelam commented May 1, 2017

Yep. That would work too.

@bricelam

This comment has been minimized.

Member

bricelam commented May 1, 2017

(assuming you swapped the apostrophes and quotes)

@bricelam

This comment has been minimized.

Member

bricelam commented May 1, 2017

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

@bricelam bricelam modified the milestones: 2.0.0, 2.0.0-preview2 May 16, 2017

@bricelam bricelam modified the milestones: Backlog, 2.0.0 Jun 2, 2017

@pjmagee

This comment has been minimized.

pjmagee commented Oct 6, 2017

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?

@bricelam

This comment has been minimized.

Member

bricelam commented Oct 6, 2017

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

@bricelam

This comment has been minimized.

Member

bricelam commented Jan 30, 2018

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

@dkehring

This comment has been minimized.

dkehring commented Apr 24, 2018

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.

@Korigoth

This comment has been minimized.

Korigoth commented May 27, 2018

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?

@ajcvickers

This comment has been minimized.

Member

ajcvickers commented May 29, 2018

@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?

@Korigoth

This comment has been minimized.

Korigoth commented May 29, 2018

@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?

@dkehring

This comment has been minimized.

dkehring commented May 29, 2018

@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?

@Korigoth

This comment has been minimized.

Korigoth commented May 29, 2018

@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;
    }
@dkehring

This comment has been minimized.

dkehring commented May 30, 2018

@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

@dkehring

This comment has been minimized.

dkehring commented May 30, 2018

@ajcvickers Also, is your solution cross-platform?

@Korigoth

This comment has been minimized.

Korigoth commented May 30, 2018

@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.

@dkehring

This comment has been minimized.

dkehring commented May 30, 2018

@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.

@bricelam

This comment has been minimized.

Member

bricelam commented May 30, 2018

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

This issue is open, and considered when planning releases.

@dkehring

This comment has been minimized.

dkehring commented May 30, 2018

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

@ajcvickers

This comment has been minimized.

Member

ajcvickers commented May 31, 2018

@dkehring @Korigoth Thanks for the additional information.

@MatthewLymer

This comment has been minimized.

MatthewLymer commented Jun 20, 2018

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?

@f-tischler

This comment has been minimized.

f-tischler commented Jul 6, 2018

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.

@VictorioBerra

This comment has been minimized.

VictorioBerra commented Aug 23, 2018

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

@bricelam

This comment has been minimized.

Member

bricelam commented Aug 23, 2018

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)
@dkehring

This comment has been minimized.

dkehring commented Aug 23, 2018

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

@bricelam

This comment has been minimized.

Member

bricelam commented Aug 23, 2018

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.

@VictorioBerra

This comment has been minimized.

VictorioBerra commented Aug 23, 2018

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.

@bricelam

This comment has been minimized.

Member

bricelam commented Aug 23, 2018

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

@chamikasandamal

This comment has been minimized.

chamikasandamal commented Sep 26, 2018

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

@bricelam

This comment has been minimized.

Member

bricelam commented Sep 26, 2018

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

@sekulicb

This comment has been minimized.

sekulicb commented Oct 8, 2018

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.

@ajcvickers

This comment has been minimized.

Member

ajcvickers commented Oct 8, 2018

@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.

@sekulicb

This comment has been minimized.

sekulicb commented Oct 8, 2018

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.

@bricelam bricelam removed their assignment Oct 12, 2018

@bricelam bricelam changed the title from Tools: Flow arguments into IDbContextFactory to Tools: Flow arguments into IDesignTimeDbContextFactory Oct 12, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment