Entity Framework Core 3.0 Gotchas
These things caught me out
Setting up a new project from scratch, following a workshop (admiteddly for ASP.NET Core 2.2) I couldn’t get a few things to work as I was following along.
I created the following stuff:
dotnet new sln -n "MyProject"
dotnet new classlib -n "MyProject.Domain"
dotnet new mvc -n "MyProject.Web"
dotnet sln add MyProject.Domain\MyProject.Domain.csproj
dotnet sln add MyProject.Web\MyProject.Web.csproj
dotnet add MyProject.Web\MyProject.Web.csproj reference MyProject.Domain\MyProject.Domain.csproj
dotnet build
Great, ship it.
EntityFramework not installed by default
I wanted to put all the DB interactions in the class library so I added a simple model and context
using Microsoft.EntityFrameworkCore;
namespace MyProject.Domain
{
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
}
}
}
No dice… Entity Framework Core doesn’t come bundled, no biggy:
dotnet add MyProject.Domain\MyProject.Domain.csproj package Microsoft.EntityFrameworkCore
error: Package Microsoft.EntityFrameworkCore 3.0.0 is not compatible with netstandard2.0 (.NETStandard,Version=v2.0). Package Microsoft.EntityFrameworkCore 3.0.0 supports: netstandard2.1 (.NETStandard,Version=v2.1)
error: Package 'Microsoft.EntityFrameworkCore' is incompatible with 'all' frameworks in project 'MyProject.Domain\MyProject.Domain.csproj
:| oh…
By default, the classlib template sets the TargetFramework to netstandard2.0, again, no biggy - edit the csproj file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>
</Project>
Success! Deployment #2!
So now it’s time for fancy things like ‘Migrations’… ooooo! My workshop tells me this:
cd MyProject.Domain
dotnet ef migrations add Initial
Could not execute because the specified command or file was not found.
Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET Core program, but dotnet-ef does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
:| oh…
EF Core 3.0 no longer ships with the CLI tool but you can add it easy enough:
dotnet tool install --global dotnet-ef --version 3.0.0
dotnet ef migrations add Initial
Startup project 'MyProject.Domain.csproj' targets framework '.NETStandard'. There is no runtime associated with this framework, and projects targeting it cannot be executed directly. To use the Entity Framework Core .NET Command-line Tools with this project, add an executable project targeting .NET Core or .NET Framework that references this project, and set it as the startup project using --startup-project; or, update this project to cross-target .NET Core or .NET Framework. For more information on using the EF Core Tools with .NET Standard projects, see https://go.microsoft.com/fwlink/?linkid=2034781
:| oh…
Google tells me I have 2 options…
- Use —startup-project ..\MyProject.Web\MyProject.Web.csproj option when creating migrations (ok, the error message above told me that, but bear with me…)
- Change MyProject.Domain to target multiple frameworks netcoreapp3.0 and netstandard2.1
First option sounds a lot less kludgey than the second, except:
dotnet ef migrations add Initial --startup-project ..\MyProject.Web\MyProject.Web.csproj
Your startup project 'MyProject.Web' doesn't reference Microsoft.EntityFrameworkCore.Design. This package is required for the Entity Framework Core Tools to work. Ensure your startup project is correct, install the package, and try again.
Now it wants me to add a reference to Microsoft.EntityFrameworkCore.Design in MyProject.Web - in my mind, all the Entity Framework stuff should be contained in the Domain project :( Suddenly, the second option isn’t quite so messy :(
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;netstandard2.1</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" />
</ItemGroup>
</Project>
And add the reference:
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet ef migrations add Initial
Unable to create an object of type 'MyDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
:| oh my days!
The best solution to this I’ve found so far is to add another nuget package to MyProject.Domain
dotnet add package Microsoft.Extensions.Configuration.Json
And create a DesignTimeDbContextFactory class like so:
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
public MyDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(@Directory.GetCurrentDirectory() + "/../MyProject.Web/appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<MyDbContext>();
var connectionString = configuration.GetConnectionString("DatabaseConnection");
builder.UseSqlite(connectionString);
return new MyDbContext(builder.Options);
}
}
It reads common configuration from the Web projects application.json so in theory it should always be configured in the same way as the actual implementation at runtime. I can’t say I’m over the moon with this workaround but it will have to do for now!