The Problem
I'm trying to setup Entity Framework Core 8 to use PostgreSQL and cannot do the initial migration. I am doing this on my local Windows machine - not a build server.
I am able to download the EntityFramework.Docs
repository from GitHub and modify and generate migrations just fine using dotnet ef migrations add TestMigration
, so I am 99% confident that the correct tooling is installed.
But I get the following error when I try to do so in my actual solution:
error MSB4057: The target "GetEFProjectMetadata" does not exist in the project.
Microsoft.EntityFrameworkCore.Tools.CommandException: Unable to retrieve project metadata. Ensure it's an SDK-style project. If you're using a custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, Use the --msbuildprojectextensionspath option.
at Microsoft.EntityFrameworkCore.Tools.Project.FromFile(String file, String buildExtensionsDir, String framework, String configuration, String runtime)
at Microsoft.EntityFrameworkCore.Tools.RootCommand.Execute(String[] _)
at Microsoft.EntityFrameworkCore.Tools.Commands.CommandBase.<>c__DisplayClass0_0.b__0(String[] args)
at Microsoft.DotNet.Cli.CommandLine.CommandLineApplication.Execute(String[] args)
at Microsoft.EntityFrameworkCore.Tools.Program.Main(String[] args)Unable to retrieve project metadata. Ensure it's an SDK-style project. If you're using a custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, Use the --msbuildprojectextensionspath option.
Because of how my company structures solutions, I have the following projects:
src/Web/
<- The WebApi host projectsrc/PostgreSQL/
<- The EF Core/PostgreSQL project that implements the IRepository
, and contains the DbContext
.src/PostgreSQL.Host/
<- I can delete this, if not needed.Originally, I tried running
dotnet ef migrations add InitializeDB --project src/PostgreSQL --startup-project src/Web
and I got the error above.
So, I added the PostgreSQL.Host project so I can try different ways of initializing the DbContext
- since some posts stated that might be the cause of the issue.
No matter what I've tried, I get the same error.
Things I've tried:
IDesignTimeDbContextFactory<MyDbContext>
in PostgreSQL with and without a normal startup flow.Microsoft.EntityFrameworkCore.Tools
, in addition to .Design
.BaseIntermediateOutputPath
and MSBuildProjectExtensionsPath
- none found.Relevant source files
dotnet ef
returns
Entity Framework Core .NET Command-line Tools 8.0.8
MyDbContext
looks like this:
public class MyDbContext : DbContext
{
public MyDbContext (DbContextOptions<MyDbContext > options)
: base(options)
{ }
// ...
}
MyDesignTimeDbContextFactory
looks like this:
// This can be removed, if not needed.
public class MyDesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
public MyDbContext CreateDbContext(string[] args)
{
// Build configuration
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("../Host.Web/appsettings.json")
.Build();
// Get connection string
var connectionString = configuration.GetSection("PostgreSQLSettings")["ConnectionString"];
// Configure DbContext options
var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
optionsBuilder.UseNpgsql(connectionString);
return new MyDbContext(optionsBuilder.Options);
}
}
PostgreSQL.csproj
looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>MyProject.PostgreSQL</RootNamespace>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
<PackageReference Include="Ulid" />
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>
</Project>
This is PostgreSQL.Host.csproj
:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\PostgreSQL\PostgreSQL.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>
</Project>
Command run is variations on
dotnet ef migrations add InitializeDB --project src/PostgreSQL --startup-project src/PostgreSQL.Host --verbose
Error:
Error MSB4057: The target "GetEFProjectMetadata" does not exist in the project.
Microsoft.EntityFrameworkCore.Tools.CommandException: Unable to retrieve project metadata. Ensure it's an SDK-style project. If you're using a custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, Use the --msbuildprojectextensionspath option.
at Microsoft.EntityFrameworkCore.Tools.Project.FromFile(String file, String buildExtensionsDir, String framework, String configuration, String runtime)
at Microsoft.EntityFrameworkCore.Tools.RootCommand.Execute(String[] _)
at Microsoft.EntityFrameworkCore.Tools.Commands.CommandBase.<>c__DisplayClass0_0.b__0(String[] args)
at Microsoft.DotNet.Cli.CommandLine.CommandLineApplication.Execute(String[] args)
at Microsoft.EntityFrameworkCore.Tools.Program.Main(String[] args)Unable to retrieve project metadata. Ensure it's an SDK-style project. If you're using a custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, Use the --msbuildprojectextensionspath optio
Any suggestions on what I'm doing wrong?
Thanks.
UPDATE
The solution has a Dicretory.Build.props file that includes:
<RepoRoot Condition="$(RepoRoot) == ''">$([MSBuild]::EnsureTrailingSlash('$(MSBuildThisFileDirectory)'))</RepoRoot>
<ArtifactsPath>$(RepoRoot)artifacts</ArtifactsPath>
If I remove the ArtifactsPath
line then the migration works.
I can't remove that line because that breaks other things in the solution build process.
So, I need to work around that setting.
I've tried setting the --msbuildprojectextensionspath to that artifacts folder manually, but it doesn't work.
Any ideas?
I figured out how to solve this.
Create an entity host/migrations project - the purpose of which is only to create migrations.
In that project, add a new Directory.Build.props
file that looks like this:
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
This file must exist in the project folder in order to override the solution level Directory.Build.props file.
Otherwise, the changes to the ArtifactsPath breaks the `dotnet ef migrations add` command.
-->
</Project>
Now you can run your dotnet ef migrations add
commands.