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

Default working directory inconsistent between dotnet run and Visual Studio #3619

Open
divega opened this issue Jun 5, 2018 · 19 comments
Open
Assignees
Labels
Bug This is a functional issue in already written code. Feature-Debugging Launching an application via F5, setting debug properties and launch profiles. Triage-Investigate Reviewed and investigation needed by dev team
Milestone

Comments

@divega
Copy link

divega commented Jun 5, 2018

(first reported at dotnet/EntityFramework.Docs#735)

It seems that there was a decision in #2239 to change the current directory to be the output directory.

I am not sure about the rationale or what the behavior was before, but what I am seeing is that (using .NET Core SDK 2.1.300, .NET Core 2.1 and Visual Studio 2017 15.7.3), the default working directory is inconsistent between executing an application in Visual Studio (using F5 or Ctrl+F5), which results in the working directory set to the output directory, and other ways, which use the project folder, like dotnet run, dotnet ef migrations commands in the CLI and even the EF Core migration tools that run in the Package Manager Console inside Visual Studio.

I am aware that it is possible to explicitly set the working directory to an absolute path using Visual Studio and that this is recorded in launch settings, which other tools pick up. This issue is about the default behavior when the working directory is not explicitly configured.

To repro, it is enough to just create a simple application that prints Directory.GetCurrentDirectory().

Here are the repro steps that shows how this impacts the location of an application's SQLite database file (based on the walkthrough at https://docs.microsoft.com/en-us/ef/core/get-started/netcore/new-db-sqlite):

Create a new console app from the command line:

mkdir ConsoleApp.SQLite
cd ConsoleApp.SQLite/
dotnet new console

Add the following EF Core packages:

dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools

(the latter is only necessary to repro the inconsistency with PMC)

Add the following sample model into Model.cs:

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

namespace ConsoleApp.SQLite
{
    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite("Data Source=blogging.db");
        }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
}

Add the following code into Program.cs:

using System;
using System.IO;

namespace ConsoleApp.SQLite
{
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine(Directory.GetCurrentDirectory());
            using (var db = new BloggingContext())
            {
                db.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
                var count = db.SaveChanges();
                Console.WriteLine("{0} records saved to database", count);
                Console.WriteLine();
                Console.WriteLine("All blogs in database:");
                foreach (var blog in db.Blogs)
                {
                    Console.WriteLine(" - {0}", blog.Url);
                }
            }
        }
    }
}

Execute the following commands to create the database:

dotnet ef migrations add InitialCreate
dotnet ef database update

Note that the database was created in the project directory.

Executing the application from the command line results in this output.

ConsoleApp.SQLite>dotnet run
C:\Users\myself\source\repos\ConsoleApp.SQLite
1 records saved to database

All blogs in database:
- http://blogs.msdn.com/adonet

Now try to execute the application from Visual Studio (F5 or Ctrl+F5). The output shows an exception indicating that the table doesn't exist. That is because opening a connection with a database file that doesn't exist creates an empty database file:

C:\Users\myself\source\repos\ConsoleApp.SQLite\bin\Debug\netcoreapp2.1

Unhandled Exception: Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> Microsoft.Data.Sqlite.SqliteException: SQLite Error 1: 'no such table: Blogs'.
   at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
   at Microsoft.Data.Sqlite.SqliteCommand.PrepareAndEnumerateStatements(Stopwatch timer)+MoveNext()
   at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior)
   at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary`2 parameterValues)
   at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
   --- End of inner exception stack trace ---
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(DbContext _, ValueTuple`2 parameters)
   at Microsoft.EntityFrameworkCore.Storage.Internal.NoopExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IReadOnlyList`1 entriesToSave)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at ConsoleApp.SQLite.Program.Main() in C:\Users\myself\source\repos\ConsoleApp.SQLite\Program.cs:line 14

Now, let's try to update the database using the EF Core PMC commands. First, drop the database from the OS command-line to make sure we will observe the default behavior of the PMC commands:

C:\Users\myself\source\repos\ConsoleApp.SQLite>dotnet ef database drop --force

Switch to the Package Manager Console inside Visual Studio, type:

PM> update-database -verbose

You will see that one of the last lines indicates:

Closing connection to database 'main' on server 'C:\Users\myself\source\repos\ConsoleApp.SQLite\blogging.db'.

cc @bricelam

@Pilchie Pilchie added Feature-Debugging Launching an application via F5, setting debug properties and launch profiles. Area-New-Project-System Bug This is a functional issue in already written code. labels Jun 8, 2018
@girishnuli
Copy link

girishnuli commented Jun 9, 2018

Any temporary work around for this issue? Can we use a fixed path for the database file?

I am facing this issue on mac

@Starkie
Copy link

Starkie commented Jun 9, 2018

@girishnuli : As we found out in dotnet/EntityFramework.Docs#735, you could change the working directory of the project (Project Settings > Debug > Working Directory) to its root folder.

@davkean davkean added this to the 16.0 milestone Jun 21, 2018
@davkean
Copy link
Member

davkean commented Jun 21, 2018

@BillHiebert I was under the (incorrect) impression that with #2239 we were changing the current directory to be consistent inside and outside of Visual Studio, while at same time being consistent with .NET Framework projects. It looks like however, that while we changed it change it to be consistent with desktop inside of Visual Studio, it now inconsistent with itself outside of Visual Studio.

Sounds like we need to change this design slightly. At minimum we should consistent with ourselves in all cases, and stretch goal is to be consistent with desktop.

@dsplaisted Do you have an opinion on this?

@davkean
Copy link
Member

davkean commented Jun 21, 2018

I don't want to change this in an update - it will lead to confusion. I'd prefer to do this in a major update.

@dsplaisted
Copy link
Member

I think that when running dotnet from the command line, we don't normally set the current directory. So if you do dotnet run, then the current directory will be the project directory, but if you do dotnet run -project ..\MyOtherProject\MyOtherProject.csproj, then the current directory won't be the project directory. The launchSettings.json can specify a working directory, which I think dotnet run would use, but it's not set by default.

I don't have all the cases in my head, but I think we should probably undo the change made in #3073, at least for some subset of projects (SDK-style perhaps). That means it would be inconsistent with desktop projects, but mostly consistent within .NET Core projects between VS and the command line.

@kayjtea
Copy link

kayjtea commented Oct 1, 2018

Direct support for making the working directory the same as the project root would be a decent compromise. If you want this behavior after the #3073 "fix", VS requires you to set an absolute path, which means the setting cannot be checked in to source code control. The current behavior of #3073 also means VS is the odd-ball on a mix-IDE team: dotnet-cli, VS Code, and JetBrains Rider all behave like "dotnet run" from the project root.

@vitek-karas
Copy link
Member

Any plans on this? This also makes it rather tricky to use dynamic loading (like Assembly.LoadFrom) with any consistency between VS and CLI.

@davkean
Copy link
Member

davkean commented Nov 27, 2018

@vitek-karas We do plan on addressing this, on your particular issue, you should not be relying on "Current Directory" to find dlls, or other things related to your project; anyone can launch your exe with a different working directory.

@jjmew jjmew modified the milestones: 16.0, 16.X Feb 13, 2019
@akeeton
Copy link

akeeton commented May 2, 2019

I found a workaround from https://github.com/aspnet/websdk/issues/238:

This can easily be fixed by setting RunWorkingDirectory in the project file:

<PropertyGroup>
  <RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
</PropertyGroup>

@thothothotho
Copy link

The current working directory is absolutely essential on unix, maybe not that used on windows.
From the code point of view:

cd someDir
dotnet run --project .../Foo.csproj

and

cd someDir
/path/To/Binary/Foo

should not expose different behavior (the CWD should be the same (.../someDir).

This is the universal convention respected by all good citizens in the unix world (python, ruby, go, ...).
It has some nice advantages on its own. For instance, you can call dotnet run from a script without the need to compile a project first. This turns C# into some kind of a scripting facilily, with zero deployment cost.

And if the C# parser would just ignore the first line of a file if it starts by a #! sequence, this would open a whole new world of possibilities.

But first, make dotnet run preserves the current directory.

@davkean davkean assigned swesonga and unassigned jmarolf Jan 27, 2020
@davkean davkean added this to the 16.7 milestone Jan 27, 2020
@davkean
Copy link
Member

davkean commented Jan 28, 2020

Triage: We need investigation on what the correct change here is, as its unclear. Please factor in #5053 when you do this.

@davkean davkean added the Triage-Investigate Reviewed and investigation needed by dev team label Jan 28, 2020
@rlalance
Copy link

Just got hit by this issue too.
Rider is running, but using my database in the project folder instead of the package folder.

@jjmew jjmew modified the milestones: 16.7, 16.x Jun 23, 2020
@alexsandro-xpt
Copy link

@akeeton Nice, it is working for me, thanks! but it is some kind of bug at Visual Studio?

@Ruin0x11
Copy link

Ruin0x11 commented Oct 4, 2021

Any updates on this? I just got hit by this issue also, and the RunWorkingDirectory workaround does not work for me. I want the working directory to be the output folder of the build assets, not the project root directory.

@rlalance
Copy link

rlalance commented Oct 4, 2021

This seems to have been closed without a resolution?

@GeeWee
Copy link

GeeWee commented Oct 4, 2021

The issue is still open :)

@drewnoakes drewnoakes modified the milestones: 16.x, 17.x Oct 6, 2021
@wlwl2
Copy link

wlwl2 commented Oct 14, 2021

@akeeton's solution solved my issue, which was that dotnet run was using a different path than the .sln output for an I/O operation.

@swesonga swesonga removed their assignment Nov 2, 2021
@swesonga swesonga removed the Triage-Investigate Reviewed and investigation needed by dev team label Nov 2, 2021
@jjmew
Copy link
Contributor

jjmew commented Nov 16, 2021

lets wait for the outcome of #5053

@frankbuckley
Copy link

@davkean wrote:

@vitek-karas We do plan on addressing this, on your particular issue, you should not be relying on "Current Directory" to find dlls, or other things related to your project; anyone can launch your exe with a different working directory.

FWIW, I hit this issue using System.CommandLine to bind a Argument<DirectoryInfo>.

DirectoryInfo uses Path.GetFullPath which uses current directory.

I made no explicit decision about using current directory or otherwise - I assumed initializing with a relative path would behave the same regardless of how I start the app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug This is a functional issue in already written code. Feature-Debugging Launching an application via F5, setting debug properties and launch profiles. Triage-Investigate Reviewed and investigation needed by dev team
Projects
None yet
Development

No branches or pull requests