Getting Started with Entity Framework Core: From Zero to Working DbContext

When I first worked on an internal reporting tool for our financial system, I was greeted by thousands of lines of raw ADO.NET, SqlCommand, DataReader, and manual mapping. It worked, but it was hard to maintain and impossible to test. I proposed rewriting the data access layer using Entity Framework Core.

If you’re starting from zero or coming from ADO.NET like I did, this post will help you get a working DbContext from scratch. I’ll walk you through it with real-world experience, code samples, and useful tips we picked up while implementing it in production.

What Is Entity Framework Core?

Entity Framework Core (EF Core) is Microsoft’s official Object-Relational Mapper (ORM) for .NET. It bridges the gap between object-oriented programming and relational databases.

With EF Core, you can:

  • Map database tables to C# classes (also called models or entities).
  • Use LINQ to write expressive, readable, and strongly-typed queries instead of raw SQL.
  • Keep your database schema in sync with your code using migrations.
  • Track and persist changes to your data in a structured and manageable way.

Think of EF Core as a translator. On one side, it speaks fluent C#, and on the other, it understands SQL and your database dialect.

Some key features include:

  • Cross-platform support – works on Windows, Linux, macOS.
  • Provider-based – supports SQL Server, PostgreSQL, MySQL, SQLite, and even in-memory databases.
  • Lightweight and extensible – you can plug in interceptors, logging, custom type conversions.
  • Change tracking – it automatically tracks which entities are modified and generates the right SQL to update the database.

From experience: We used EF Core to replace fragile manual SQL statements. It helped reduce bugs and simplify onboarding for new developers. Instead of explaining SQL joins every time, we just walked them through entity relationships in C#.

EF Core is not just for CRUD apps. You can use it in enterprise applications with clean architecture, CQRS, and service-oriented patterns too.

Let’s Get Practical

Now that you understand what Entity Framework Core is and what problems it solves, the next section will walk you through how to set up everything, from installing required packages to creating your first working DbContext.

You’ll see step-by-step instructions, code samples, and some practical tips based on real project experience.

Prerequisites
  • .NET 9 SDK installed
  • Visual Studio 2022+, Rider, or VS Code
  • SQL Server or SQLite
  • Basic understanding of C# and object-oriented design
Step 1: Install EF Core Packages

Before using EF Core, install the necessary NuGet packages. Assuming SQL Server as the database:

Bash
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.DependencyInjection

If you’re experimenting locally or building a small tool, you can use SQLite:

Bash
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
PackagePurpose
Microsoft.EntityFrameworkCore.SqlServerEF Core provider for SQL Server. Enables you to run queries against SQL Server DB.
Microsoft.EntityFrameworkCore.ToolsProvides support for dotnet ef commands used to scaffold and manage migrations.
Microsoft.Extensions.ConfigurationReads your connection strings from appsettings.json, environment variables, etc.
Microsoft.Extensions.DependencyInjectionEnables dependency injection for your DbContext, repositories, and services.
Microsoft.EntityFrameworkCore.SqliteOptional provider to work with lightweight SQLite DBs for quick testing.
Step 2: Create Your Entity Models

An entity is a class that maps to a table in your database, and each property in the class maps to a column. Let’s say we are managing merchants and their transactions. Create models like below:

C#
public class Merchant
{
    public Guid MerchantId { get; set; }
    public string Name { get; set; } = string.Empty;
    public ICollection<Transaction> Transactions { get; set; } = new List<Transaction>();
}

public class Transaction
{
    public Guid TransactionId { get; set; }
    public Guid MerchantId { get; set; }
    public decimal Amount { get; set; }
    public DateTime DateCompleted { get; set; }

    public Merchant Merchant { get; set; } = null!;
}

In our real system, we also had fields like BankReferenceNo, SettlementBatchId, and foreign keys to other tables. Start with the minimum and expand.

Step 3: Create a Working DbContext

The DbContext is the brain of EF Core. It represents a session with your database.

Responsibilities:

  • Opening/closing database connection
  • Tracking changes to your objects
  • Translating LINQ queries into SQL
  • Executing Create/Update/Delete operations

If you come from SQL background, think of DbContext as your main access object, like a smart wrapper around a connection and transaction.

In our system, we abstracted it behind an interface (IAppDbContext) to isolate business logic from database implementation. That allowed us to mock and test services more easil

C#
public class AppDbContext : DbContext
{
    public DbSet<Merchant> Merchants => Set<Merchant>();
    public DbSet<Transaction> Transactions => Set<Transaction>();

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Merchant>().HasKey(m => m.MerchantId);
        modelBuilder.Entity<Transaction>()
            .HasKey(t => t.TransactionId);

        modelBuilder.Entity<Transaction>()
            .HasOne(t => t.Merchant)
            .WithMany(m => m.Transactions)
            .HasForeignKey(t => t.MerchantId);
    }
}

TIP: For larger domains, split your entity configurations into separate files using IEntityTypeConfiguration.

Step 4: Configure DbContext in Program.cs

EF Core uses .NET Dependency Injection. In .NET 9, you’ll likely have this in Program.cs:

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

And in appsettings.json:

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

If your system is multi-environment like ours (DEV, SIT, UAT, PROD), consider injecting IConfiguration and use a factory pattern for DbContext.

Step 5: Add Migration and Create the DB

After defining your model and DbContext:

Bash
dotnet ef migrations add InitialCreate
dotnet ef database update

EF Core generates SQL scripts and applies them to your database. This saved us a lot of deployment effort, especially during UAT testing.

Step 7: Use DbContext in Code

Here’s how you can seed and fetch data:

C#
using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

    var merchant = new Merchant { MerchantId = Guid.NewGuid(), Name = "Alpha" };
    db.Merchants.Add(merchant);
    await db.SaveChangesAsync();

    var recentTxns = await db.Transactions
        .Where(t => t.DateCompleted > DateTime.UtcNow.AddDays(-30))
        .ToListAsync();
}

Use AsNoTracking() if you’re only reading to improve performance.

Pro Tips from Experience

  • Always version your database using EF Migrations.
  • Abstract DbContext behind IAppDbContext for testability.
  • Avoid accessing DbSet directly from controller.
  • Handle exceptions from SaveChangesAsync() properly.

Common Mistakes to Avoid

  • Not calling await db.SaveChangesAsync() after Add()/Update()
  • Forgetting to add foreign key relationships
  • Using DbContext outside of using or DI scope (causes memory leaks)
  • Calling .ToList() before applying filters

Wrapping Up

EF Core is powerful but easy to get started with. If you set up your DbContext properly, most of the work is simplified—from querying to migrations.

In our project, shifting from raw SQL to EF Core reduced bugs, improved test coverage, and helped us onboard new devs faster.

References

Assi Arai
Assi Arai
Articles: 33