Chapter 01 · EF Core Foundations, .NET Integration, Providers, and the First Lab
Install the CLI and Packages, Choose a Database Provider, and Verify the Toolchain
Install and verify the EF Core CLI, aligned project packages, and a relational provider as separate toolchain layers, then diagnose design-time failures with evidence instead of reinstalling blindly.
Learning outcomes
ServiceHub has a supported version contract; now it needs a toolchain that every developer and CI agent can reproduce. The goal is not “install some NuGet packages until IntelliSense turns green.” We will separate SDK tooling, the EF CLI tool, project design-time services, the relational provider, and the database engine, then verify each layer independently.
Install dotnet-ef as a repository-local tool and explain when a global installation is acceptable.
Add the EF Core Design package and a relational provider with aligned 10.0.11 versions.
Choose SQLite for the mandatory cross-platform lab while understanding when SQL Server, PostgreSQL, MySQL/MariaDB, or Oracle providers are appropriate.
Verify restore, build, runtime provider registration, and design-time tooling independently.
Diagnose missing Design-package, provider-registration, and version-drift failures without randomly reinstalling the SDK.
The mandatory first lab uses Microsoft.EntityFrameworkCore.Sqlite 10.0.11 with SQLite 3.46.1 or later. Microsoft’s provider documentation currently lists both SQLite and SQL Server providers for EF Core 10. Other provider ecosystems have independent schedules and must be checked against their own compatibility documentation before adoption.
1. The toolchain has five separate pieces
| Piece | Installed where | Purpose |
|---|---|---|
| .NET 10 SDK | Developer/CI machine | Build, restore, templates, dotnet CLI and compilers |
dotnet-ef |
Global tool store or repository tool manifest | Design-time commands such as migrations and reverse engineering |
Microsoft.EntityFrameworkCore.Design |
Project PackageReference | Design-time services consumed by EF tooling |
| Database provider | Project PackageReference | Runtime + migration translation for a database family |
| Database engine/library | Local file/process/container/server | Actually executes SQL and enforces relational semantics |
A failure in one piece does not imply the others are broken. For
example, dotnet ef --version can succeed even when
the startup project lacks
Microsoft.EntityFrameworkCore.Design. Conversely, a
project can compile and run normal EF queries without
dotnet-ef installed because the CLI tool is not a
runtime dependency.
2. Prefer a local dotnet-ef tool for repository reproducibility
A global tool is convenient on a personal workstation, but its
version can drift independently from the repository. A
local tool is declared in
.config/dotnet-tools.json and restored with the
repository. That makes CI and developer setup explicit.
dotnet new tool-manifestdotnet tool install dotnet-ef --version 10.0.11dotnet tool list --localdotnet ef --version
Commit the tool manifest, not a machine-specific tool cache. A
new checkout can run dotnet tool restore. If an
organization standardizes a global tool, record its required
version and still verify it in CI; “global” should not mean
“unknown.”
Visual Studio Package Manager Console commands such as Add-Migration are not the same syntax as dotnet-ef. This course prefers cross-platform dotnet CLI commands. PMC may be shown as an alternative when it adds value, but command sets will not be mixed.
3. Choose the provider from the production contract, not from LINQ familiarity
The provider determines database-specific type mappings, SQL generation, function translation, migrations operations, value generation, concurrency capabilities, and many diagnostics. Provider selection is therefore an architectural decision, not a cosmetic connection-string switch.
| Provider path | Good first use | Boundary to remember |
|---|---|---|
| Microsoft.EntityFrameworkCore.Sqlite | Cross-platform local labs, embedded applications, lightweight tests | SQLite type affinity, DDL, concurrency, functions, and SQL semantics differ from server databases. |
| Microsoft.EntityFrameworkCore.SqlServer | SQL Server/Azure SQL applications and provider-specific labs | Requires a SQL Server-compatible engine; SQL Server-specific types/features are intentional lock-in. |
| Npgsql.EntityFrameworkCore.PostgreSQL | PostgreSQL applications | Independent provider release/support matrix; PostgreSQL types/functions and migrations differ. |
| MySQL/MariaDB providers | MySQL or MariaDB applications | Vendor/community providers have independent EF/database compatibility matrices. |
| Oracle.EntityFrameworkCore | Oracle Database applications | Vendor provider, database versions, feature support, and deployment/licensing assumptions must be checked. |
SQLite is the mandatory Chapter 01 provider because it is free and requires no server process. That does not make SQLite a universal stand-in for a production SQL Server or PostgreSQL database. Chapter 23 explains why provider-realistic integration tests matter.
4. Add aligned project packages
Create the project with the course target framework, then add the provider and design-time package at the same EF patch. Explicit versions make the lab reproducible even if a newer servicing patch is released later.
dotnet new console -n ServiceHub.EfLab -f net10.0cd ServiceHub.EfLabdotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 10.0.11dotnet add package Microsoft.EntityFrameworkCore.Design --version 10.0.11dotnet restoredotnet builddotnet list package
The SQLite provider pulls the relational/core packages it needs.
Inspect the resolved package graph rather than adding every EF
package you have heard of.
Microsoft.EntityFrameworkCore.Tools is for Visual
Studio Package Manager Console; it is not required just because
you use dotnet ef.
5. Verify runtime provider registration separately from tooling
A provider package being present does not configure a
DbContext. EF Core needs provider services
registered in the context options. For the first probe, build
options directly and inspect the provider name.
using Microsoft.EntityFrameworkCore;var options = new DbContextOptionsBuilder<ProbeContext>() .UseSqlite("Data Source=toolchain-probe.db") .Options;await using var context = new ProbeContext(options);Console.WriteLine(context.Database.ProviderName);public sealed class ProbeContext(DbContextOptions<ProbeContext> options) : DbContext(options);
dotnet run should print
Microsoft.EntityFrameworkCore.Sqlite. This proves
that the built application can construct an EF context with
SQLite provider services. It does not prove migrations tooling
can construct the context at design time; that is a separate
path.
6. Deliberately wrong approach: run migrations without the Design package
Remove or omit
Microsoft.EntityFrameworkCore.Design, then ask the
EF tool to perform a project-aware design-time operation. The
tool can exist and report its own version, yet the project is
missing the services it needs.
dotnet remove package Microsoft.EntityFrameworkCore.Designdotnet ef migrations add ToolchainProbe
The exact diagnostic text can change across patches, but it
should explain that the startup project does not reference
Microsoft.EntityFrameworkCore.Design or otherwise
cannot provide required design-time services. The repair is
explicit:
dotnet add package Microsoft.EntityFrameworkCore.Design --version 10.0.11dotnet restoredotnet build
Do not respond by reinstalling Visual Studio, deleting every NuGet cache, or installing random global tools. First identify which layer failed: CLI discovery, project restore, build, design-time context creation, provider loading, or database connection.
7. Another common failure: package installed, UseSqlite unavailable
If UseSqlite is unresolved, inspect the provider
PackageReference and the
using Microsoft.EntityFrameworkCore; namespace
before assuming the database is broken. If the compiler error
appears only on one machine, compare the resolved package graph
and target framework. Compile-time provider-extension discovery
occurs before any connection to SQLite.
dotnet --versiondotnet restoredotnet builddotnet list package --include-transitivedotnet tool list --local
8. Design-time context creation is an application-construction problem
Commands such as dotnet ef migrations add need a
DbContext instance at design time. In real
applications EF tooling may obtain it through the application
service provider, a parameterless construction path, or an
IDesignTimeDbContextFactory<TContext>.
Chapter 2 teaches those patterns in detail. For now, recognize
the symptom: a successful build followed by “Unable to create a
DbContext” is not the same failure as a missing tool or
provider.
Record the full exception and startup-project/target-project selection. Multi-project solutions frequently fail because tooling executes a different startup path than the developer expected.
9. Observe provider SQL with a one-row query
Toolchain verification is stronger when it reaches the database.
The following disposable probe creates a SQLite database,
inserts one row, then prints the generated query text.
EnsureCreated is used only as a prototype probe;
migrations become the normal course schema lifecycle beginning
in Lesson 4/Chapter 15.
using Microsoft.EntityFrameworkCore;var options = new DbContextOptionsBuilder<ProbeContext>() .UseSqlite("Data Source=toolchain-probe.db") .LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information) .Options;await using var db = new ProbeContext(options);await db.Database.EnsureDeletedAsync();await db.Database.EnsureCreatedAsync();db.Items.Add(new ProbeItem { Name = "provider-ok" });await db.SaveChangesAsync();var query = db.Items.Where(x => x.Name == "provider-ok");Console.WriteLine(query.ToQueryString());Console.WriteLine(await query.CountAsync());public sealed class ProbeContext(DbContextOptions<ProbeContext> options) : DbContext(options){ public DbSet<ProbeItem> Items => Set<ProbeItem>();}public sealed class ProbeItem{ public int Id { get; set; } public string Name { get; set; } = "";}
Expected evidence includes provider command logs, a SQLite-style quoted table/column query, and a result count of one. Exact command formatting is provider/patch dependent. Treat the output as observed behavior from your declared environment, not universal SQL text.
10. Hands-on lab: build the repository-local toolchain from zero
mkdir ServiceHubEfCoursecd ServiceHubEfCoursedotnet new globaljson --sdk-version 10.0.400 --roll-forward latestPatchdotnet new tool-manifestdotnet tool install dotnet-ef --version 10.0.11dotnet new console -n ServiceHub.EfLab -f net10.0cd ServiceHub.EfLabdotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 10.0.11dotnet add package Microsoft.EntityFrameworkCore.Design --version 10.0.11dotnet restoredotnet buildcd ..dotnet tool restoredotnet ef --version
Verification checklist
-
global.jsonand.config/dotnet-tools.jsonexist at the repository root. -
dotnet --versionresolves according to the repository SDK policy. dotnet ef --versionreports 10.0.11.-
The project targets
net10.0and resolves SQLite/Design 10.0.11. -
dotnet buildsucceeds before any database command is attempted. -
A runtime probe reports
Microsoft.EntityFrameworkCore.Sqlite. - You can explain why missing Design services is different from a provider package or database-connection failure.
Check your understanding
- Why can dotnet ef --version succeed while dotnet ef migrations add fails?
- What benefit does a local tool manifest provide over an unpinned global dotnet-ef?
- Why is SQLite the Chapter 01 baseline but not a promise of provider portability?
- Do you need Microsoft.EntityFrameworkCore.Tools to use dotnet-ef?
- What should you inspect before deleting NuGet caches in response to an EF tooling failure?
Review the answers
The CLI tool can be installed correctly while the project lacks Design services, fails to build, or cannot construct a DbContext at design time.
The manifest records the tool/version with the repository so developers and CI can restore the same CLI instead of depending on machine-global state.
It is free and cross-platform, but its type system, DDL, functions, locking, and SQL differ from other engines; provider-specific behavior must be tested separately.
No. Microsoft.EntityFrameworkCore.Tools is for Visual Studio Package Manager Console. dotnet-ef uses the .NET CLI tool plus Microsoft.EntityFrameworkCore.Design in the project.
Inspect SDK/target framework, restore/build diagnostics, direct/transitive packages, tool version/location, design-time context creation, and provider registration first.
11. Production judgment and next bridge
Toolchain reproducibility is a supply-chain and operations concern. Pin what must be reproducible, restore packages/tools from approved sources, patch supported components deliberately, and keep project/runtime/design-time responsibilities separate. For provider adoption, evaluate maintainer, license, support, EF major compatibility, database versions, and the behaviors your application actually uses.
Nothing in this lesson requires a paid database or IDE. SQL
Server, PostgreSQL, MySQL/MariaDB, and Oracle will appear when
their semantics matter, but the mandatory path remains
free/local. Lesson 4 now uses this verified SQLite toolchain to
build the first real ServiceHubContext, migration,
insert, LINQ query, SaveChangesAsync, and
generated-SQL inspection.
Authoritative references
- Installing Entity Framework Core — provider packages plus CLI/PMC tooling distinctions
- EF Core tools reference (.NET CLI) — tool installation, restore, migrations, scaffolding, and design-time commands
- Database providers — provider ecosystem, maintainers, and major-version compatibility warning
- SQLite provider — Microsoft-maintained SQLite provider and limitations
- SQL Server provider — Microsoft SQL Server/Azure SQL provider
- dotnet tool install — global/local .NET tool installation semantics
- dotnet-ef 10.0.11 — current stable tool package
- Microsoft.EntityFrameworkCore.Design 10.0.11 — current stable design-time package