How to Install and Configure EF Core in .NET 9 Projects

If you’re reading this, chances are you’re either just starting with Entity Framework Core or you’re moving an older project to .NET 9 and want a quick but reliable setup guide. I’ve been in the same boat before, when we were starting a back-office system for handling merchant settlements, we needed a lightweight but powerful ORM that would scale as the product grows. EF Core was our go-to, and I’ve never regretted that decision (well, except during some migration issues but that’s a story for another post!).

Today, I’ll walk you through how I install and configure EF Core in .NET 9 projects. This guide is not just “hello world” level. I’ll also share some real mistakes I made so you can avoid them.


Prerequisites

Before we dive in, make sure you have:

  • .NET 9 SDK (Preview or latest RC)
  • A code editor (I use Visual Studio 2022)
  • SQL Server 2022 (you can use LocalDB, Docker, or your favorite flavor)
  • Basic knowledge of C#

Step 1: Creating a New Project

Let’s start by creating a new Web API project. You can also use Blazor or Console depending on your use case.

Bash
dotnet new webapi -n MyEfCoreApp
cd MyEfCoreApp

In my case, this is a service that exposes APIs for managing inventory and sales data. Most of the logic relies on DB access, so EF Core is critical here.

Step 2: Installing EF Core NuGet Packages

For SQL Server, we need a few essential packages.

Bash
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools

Why all three?

  • SqlServer: the main provider
  • Design: required for migrations
  • Tools: gives you CLI commands like dotnet ef

In our old project, we forgot to install Design, and we spent a whole hour wondering why dotnet ef migrations add kept failing.

Step 3: Creating the DbContext and Entity Models

At this point, we’ll define our data structure, basically telling EF Core what tables and columns we want in our database.

Let’s start with a simple entity Product.

C#
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

This is what we call a POCO class or Plain Old CLR Object. It just means a simple C# class without any base classes or fancy dependencies.

  • Id: This will be automatically treated as the primary key by EF Core. No need to decorate with attributes like [Key] unless you want to be explicit.
  • Name: A required text field. We’ll make sure this has a max length later.
  • Price: Standard decimal field, good for monetary values. EF Core maps this to decimal(18,2) by default in SQL Server.

In my inventory system, this was part of the product catalog. Each record in the table represented a unique SKU (Stock Keeping Unit).

Next, let’s create the DbContext.

C#
public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}

    public DbSet<Product> Products { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>()
            .Property(p => p.Name)
            .IsRequired()
            .HasMaxLength(100);
    }
}

Think of DbContext as the middleman between your C# classes and your database.

  • AppDbContext inherits from DbContext, this gives us all the power to query and save data.
  • The constructor accepts DbContextOptions, this is where you inject your connection string later.
  • DbSet acts like a virtual table. This line tells EF Core: “Hey, expect a Products table in the database, and it should match the Product class structure.”

Now, let’s talk about OnModelCreating.

This is where we configure the finer details of how the model maps to the database.

C#
modelBuilder.Entity<Product>()
    .Property(p => p.Name)
    .IsRequired()
    .HasMaxLength(100);

Here, we’re telling EF Core:

  • Name must have a value — no nulls allowed (IsRequired()).
  • Limit the length to 100 characters — this is to avoid storing too much junk data by accident.

I learned the importance of HasMaxLength() the hard way, one of our old tables didn’t have this, and people started typing long product descriptions into the Name field. Eventually, we had to trim them manually with a script.

Here’s a recap of why we do all this:

ElementPurpose
ProductMaps to a table (e.g. Products)
DbContextThe brain of EF Core; handles queries, inserts, migrations
DbSetThink of this like SELECT * FROM Products
OnModelCreatingConfigure columns, relationships, constraints programmatically

This setup is now ready to connect with a SQL Server database in the next step. You can now start building real-world features like product listing, search, and CRUD without writing raw SQL.

Step 4: Configure EF Core in Program.cs (or Startup.cs)

In .NET 9, most projects are using the minimal hosting model. Here’s how you wire up the DbContext:

C#
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();

And don’t forget the appsettings.json:

JSON
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=MyEfCoreDb;Trusted_Connection=True;TrustServerCertificate=True;"
  }
}

In one of my older projects, I forgot to add TrustServerCertificate=true; when using SSL on local SQL Server, took me an entire evening to figure out the trust issue.

Step 5: Run Your First Migration

Let’s generate the database schema.

Bash
dotnet ef migrations add InitialCreate
dotnet ef database update

This will:

  • Create a Migrations folder with a snapshot and SQL
  • Apply that schema to your SQL Server DB

Organizing Cleanly with a Real Project Setup

In one production project, we split the layers:

  • MyApp.Domain: entities, enums
  • MyApp.Infrastructure: DbContext, configurations
  • MyApp.WebApi: controllers, service DI, config

Sample structure:

Plaintext
src/

├── MyApp.Domain/
│   └── Entities/Product.cs

├── MyApp.Infrastructure/
│   └── DbContext/AppDbContext.cs

└── MyApp.WebApi/
    └── Program.cs

In MyApp.Infrastructure, you can also split EF configuration like this:

C#
public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {
        builder.Property(p => p.Name).IsRequired().HasMaxLength(100);
    }
}

Then in OnModelCreating:

C#
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);

Makes your DbContext file less bloated.


Common Errors and My Lessons Learned

  1. “No provider has been configured”. Usually caused by missing UseSqlServer or not registering the DbContext.
  2. “No migrations found”. Happened to me when I ran dotnet ef in the wrong folder.
  3. Connection timeout. Check if your SQL Server is running, especially in Docker or Azure.

Final Checklist Before You Go

  • Installed all required NuGet packages
  • Created DbContext and models
  • Configured connection string and DI
  • Ran initial migration
  • Verified table creation

References and Credits

  • Official EF Core Docs: https://learn.microsoft.com/en-us/ef/core
  • EF Core GitHub: https://github.com/dotnet/efcore
  • Connection string help: https://www.connectionstrings.com/sql-server
  • Inspiration from my past project.

I hope this post helps you get started with EF Core on .NET 9 easily. Everything I wrote here is based on actual experiences, good and bad ones. If you encounter issues along the way, don’t panic. Every error you get is part of the learning.

Assi Arai
Assi Arai
Articles: 33