Chapter 2: First EF Core Application - IndianTechnoEra
Latest update Android YouTube

Chapter 2: First EF Core Application

Welcome to your first hands-on chapter! In this chapter, we will build a working EF Core application from scratch. You'll create a console application, define your first entity, set up a DbContext, and save data to a real SQL Server database. By the end of this chapter, you'll have a functioning EF Core application that you can use as a foundation for all future chapters.

2.1 Project Setup

Let's start by creating a new .NET Console Application. Open your terminal, command prompt, or PowerShell.

Step 1: Create a new console application


dotnet new console -n EFCoreTutorial

cd EFCoreTutorial

This creates a new folder called EFCoreTutorial with a basic console application inside.

Step 2: Open the project in Visual Studio or VS Code

If you're using Visual Studio, open the .csproj file or the folder. If you're using VS Code, simply open the folder.

Step 3: Explore the initial project structure

Your project should look like this:


EFCoreTutorial/

├── Program.cs

├── EFCoreTutorial.csproj

└── obj/

Program.cs contains the entry point of our application. We'll modify this file later.


2.2 Installing Required NuGet Packages

EF Core functionality comes via NuGet packages. We need three essential packages:

Package Name Purpose
Microsoft.EntityFrameworkCore The core EF Core library. Required for every EF Core project.
Microsoft.EntityFrameworkCore.SqlServer The SQL Server database provider. Translates LINQ to SQL Server-specific SQL.
Microsoft.EntityFrameworkCore.Tools Enables EF Core commands in the Package Manager Console (for migrations, scaffolding, etc.).

Install using .NET CLI:


dotnet add package Microsoft.EntityFrameworkCore

dotnet add package Microsoft.EntityFrameworkCore.SqlServer

dotnet add package Microsoft.EntityFrameworkCore.Tools

Install using Package Manager Console (Visual Studio):


Install-Package Microsoft.EntityFrameworkCore

Install-Package Microsoft.EntityFrameworkCore.SqlServer

Install-Package Microsoft.EntityFrameworkCore.Tools

Verify installation:

After installation, open your .csproj file. You should see something like:


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>

    <OutputType>Exe</OutputType>

    <TargetFramework>net8.0</TargetFramework>

    <ImplicitUsings>enable</ImplicitUsings>

    <Nullable>enable</Nullable>

  </PropertyGroup>

  <ItemGroup>

    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />

    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />

    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0" />

  </ItemGroup>

</Project>


2.3 Defining Your First Entity Class

In EF Core, an entity is a C# class that maps to a database table. Let's create a Book entity for our book store.

Step 1: Create a Models folder

It's good practice to organize your entities in a Models or Entities folder.


mkdir Models

Step 2: Create the Book class

Create a new file Book.cs inside the Models folder with the following content:


using System.ComponentModel.DataAnnotations;

namespace EFCoreTutorial.Models;

public class Book

{

    // Primary Key - By convention, 'Id' or 'BookId' is automatically detected as the PK

    public int Id { get; set; }

    

    // [Required] attribute makes this column NOT NULL in the database

    [Required]

    // [MaxLength] sets the maximum length of the string column

    [MaxLength(200)]

    public string Title { get; set; }

    

    // The '?' makes this property optional (nullable)

    public string? Author { get; set; }

    

    // [DataType] provides additional metadata for scaffolding

    [DataType(DataType.Currency)]

    public decimal Price { get; set; }

    

    // Nullable DateTime means this column can be NULL in the database

    public DateTime? PublishedOn { get; set; }

    

    // We'll add more properties in future chapters

    public string? ISBN { get; set; }

}

Understanding the Code:

Code Explanation
public int Id { get; set; } By convention, a property named Id or BookId becomes the primary key. It will be an auto-incrementing identity column in SQL Server.
[Required] This data annotation ensures the column is NOT NULL in the database. The property becomes required.
[MaxLength(200)] Sets the maximum length of the string column to 200 characters. In SQL Server, this becomes nvarchar(200).
string? Author The ? indicates this property is nullable. The corresponding database column will allow NULL values.
[DataType(DataType.Currency)] This provides metadata for UI scaffolding. It doesn't affect the database schema directly.

2.4 Creating the Database Context (DbContext)

The DbContext class is the most important class in EF Core. It represents a session with the database and provides APIs for querying and saving entities.

Step 1: Create a Data folder


mkdir Data

Step 2: Create the AppDbContext class

Create a new file AppDbContext.cs inside the Data folder:


using Microsoft.EntityFrameworkCore;

using EFCoreTutorial.Models;

namespace EFCoreTutorial.Data;

public class AppDbContext : DbContext

{

    // DbSet represents a database table

    // Querying Books gives you data from the Books table

    public DbSet<Book> Books { get; set; }

    

    // The OnConfiguring method is where we configure the database provider

    // and connection string

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

    {

        // Define the connection string to SQL Server LocalDB

        // This is for development only - in production, this comes from configuration

        string connectionString = @"Server=(localdb)\mssqllocaldb;Database=EFCoreBookStore;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True;";

        

        // Tell EF Core to use SQL Server with this connection string

        optionsBuilder.UseSqlServer(connectionString);

        

        // OPTIONAL: Log SQL queries to the console

        // This is incredibly useful for debugging and learning

        optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);

        

        // OPTIONAL: Enable sensitive data logging (shows parameter values)

        // Only use this in development, never in production!

        // optionsBuilder.EnableSensitiveDataLogging();

    }

}

Understanding the Connection String:

Part Meaning
Server=(localdb)\mssqllocaldb Connects to SQL Server Express LocalDB, which installs with Visual Studio. It's perfect for development.
Database=EFCoreBookStore The name of the database. EF Core will create it if it doesn't exist (when we call EnsureCreated or run migrations).
Trusted_Connection=True Uses Windows authentication (your current Windows user).
MultipleActiveResultSets=true Allows multiple queries on the same connection. Useful for scenarios with nested data readers.
TrustServerCertificate=True Bypasses SSL certificate validation for local development.

2.5 Putting It All Together - Program.cs

Now let's use our AppDbContext to add a book to the database. Replace the contents of Program.cs with:


using EFCoreTutorial.Data;

using EFCoreTutorial.Models;

// 'using' statement ensures the DbContext is properly disposed

// This closes the database connection when we're done

using var db = new AppDbContext();

Console.WriteLine("Ensuring database is created...");

// This creates the database if it doesn't exist

// In the next chapter, we'll replace this with Migrations

db.Database.EnsureCreated();

// Create a new book object

var newBook = new Book

{

    Title = "Clean Code: A Handbook of Agile Software Craftsmanship",

    Author = "Robert C. Martin",

    Price = 45.99m,

    PublishedOn = new DateTime(2008, 8, 1),

    ISBN = "978-0132350884"

};

// Add the book to the DbSet

// This marks it for insertion but doesn't execute SQL yet

Console.WriteLine("Adding book to change tracker...");

db.Books.Add(newBook);

// SaveChanges() executes the INSERT SQL command

Console.WriteLine("Saving changes to database...");

int recordsWritten = db.SaveChanges();

Console.WriteLine($"Success! {recordsWritten} record(s) written to database.");

// Let's verify by reading the data back

Console.WriteLine("\nReading books from database...");

var books = db.Books.ToList();

foreach (var book in books)

{

    Console.WriteLine($"ID: {book.Id}, Title: {book.Title}, Author: {book.Author}, Price: {book.Price:C}, Published: {book.PublishedOn:yyyy-MM-dd}, ISBN: {book.ISBN}");

}

Console.WriteLine("\nPress any key to exit...");

Console.ReadKey();


2.6 Running the Application

Step 1: Run the application


dotnet run

Expected Output:


Ensuring database is created...

Adding book to change tracker...

Saving changes to database...

info: Microsoft.EntityFrameworkCore.Database.Command[20101]

      Executed DbCommand (27ms) [Parameters=[@p0='?' (DbType=String), @p1='?' (DbType=String), @p2='?' (DbType=Decimal), @p3='?' (DbType=DateTime2), @p4='?' (DbType=String)], CommandType='Text', CommandTimeout='30']

      SET NOCOUNT ON;

      INSERT INTO [Books] ([Author], [ISBN], [Price], [PublishedOn], [Title])

      VALUES (@p0, @p1, @p2, @p3, @p4);

      SELECT [Id]

      FROM [Books]

      WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();

Success! 1 record(s) written to database.

Reading books from database...

info: Microsoft.EntityFrameworkCore.Database.Command[20101]

      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']

      SELECT [b].[Id], [b].[Author], [b].[ISBN], [b].[Price], [b].[PublishedOn], [b].[Title]

      FROM [Books] AS [b]

ID: 1, Title: Clean Code: A Handbook of Agile Software Craftsmanship, Author: Robert C. Martin, Price: $45.99, Published: 2008-08-01, ISBN: 978-0132350884

Press any key to exit...

What just happened?

  1. Database Creation: EnsureCreated() checked if the database existed. Since it didn't, it created both the database and the Books table based on your Book entity.
  2. Change Tracking: When you called db.Books.Add(newBook), EF Core started tracking the newBook entity and marked it as Added.
  3. SQL Generation: When you called SaveChanges(), EF Core generated an INSERT statement with parameters for each property.
  4. Identity Value: After insertion, EF Core retrieved the auto-generated Id value from the database and updated your newBook object.
  5. Querying: The db.Books.ToList() generated a SELECT statement and materialized the results back into Book objects.

2.7 Verifying the Database

Let's verify that our data is actually in SQL Server.

Using SQL Server Management Studio (SSMS):

  1. Open SSMS
  2. Connect to: (localdb)\mssqllocaldb
  3. Expand Databases - you should see EFCoreBookStore
  4. Expand Tables - you should see dbo.Books
  5. Right-click dbo.Books and select "Select Top 1000 Rows"

You'll see your book data in the grid.

Using SQL Query:

You can also run this query:


SELECT * FROM Books

Result:

Id Title Author Price PublishedOn ISBN
1 Clean Code: A Handbook of Agile Software Craftsmanship Robert C. Martin 45.99 2008-08-01 978-0132350884

2.8 Understanding What EF Core Generated

Let's look at the actual database schema that EF Core created:

Table Structure:


CREATE TABLE [dbo].[Books] (

    [Id]          INT            IDENTITY (1, 1) NOT NULL,

    [Author]      NVARCHAR (MAX) NULL,

    [ISBN]        NVARCHAR (MAX) NULL,

    [Price]       DECIMAL (18, 2) NOT NULL,

    [PublishedOn] DATETIME2 (7)  NULL,

    [Title]       NVARCHAR (200)  NOT NULL,

    CONSTRAINT [PK_Books] PRIMARY KEY CLUSTERED ([Id] ASC)

);

Key Observations:

Our C# Code Generated SQL Explanation
public int Id { get; set; } [Id] INT IDENTITY(1,1) NOT NULL Primary key with auto-increment (identity)
[Required] on Title [Title] NVARCHAR(200) NOT NULL Column becomes NOT NULL
[MaxLength(200)] on Title NVARCHAR(200) String length is limited to 200
string? Author [Author] NVARCHAR(MAX) NULL Nullable string becomes NULL column
decimal Price [Price] DECIMAL(18,2) NOT NULL Default decimal precision (18,2)
DateTime? PublishedOn [PublishedOn] DATETIME2(7) NULL DateTime2 is the modern DateTime type

2.9 Adding More Books (Experiment)

Let's modify Program.cs to add multiple books and see change tracking in action:


using EFCoreTutorial.Data;

using EFCoreTutorial.Models;

using var db = new AppDbContext();

// Ensure database exists

db.Database.EnsureCreated();

// Create multiple books

var booksToAdd = new[]

{

    new Book

    {

        Title = "The Pragmatic Programmer",

        Author = "David Thomas",

        Price = 49.99m,

        PublishedOn = new DateTime(1999, 10, 20),

        ISBN = "978-0201616224"

    },

    new Book

    {

        Title = "Code Complete",

        Author = "Steve McConnell",

        Price = 54.99m,

        PublishedOn = new DateTime(2004, 6, 9),

        ISBN = "978-0735619678"

    },

    new Book

    {

        Title = "Design Patterns",

        Author = "Erich Gamma",

        Price = 59.99m,

        PublishedOn = new DateTime(1994, 10, 21),

        ISBN = "978-0201633610"

    }

};

Console.WriteLine($"Adding {booksToAdd.Length} books...");

db.Books.AddRange(booksToAdd);

int recordsWritten = db.SaveChanges();

Console.WriteLine($"Added {recordsWritten} books.");

// Read and display all books

Console.WriteLine("\nAll books in database:");

foreach (var book in db.Books.OrderBy(b => b.Title))

{

    Console.WriteLine($"- {book.Title} by {book.Author} (${book.Price})");

}

Key Points:

  • AddRange() adds multiple entities at once
  • OrderBy() sorts the results
  • All SQL commands are batched into a single database round-trip

2.10 Common Issues and Solutions

Issue 1: Connection string errors

Error: Cannot connect to (localdb)\mssqllocaldb

Solution: Ensure LocalDB is installed and running:


sqllocaldb info

sqllocaldb start mssqllocaldb

Issue 2: Package version mismatches

Error: System.TypeLoadException or missing methods

Solution: Ensure all EF Core packages are the same version:


dotnet add package Microsoft.EntityFrameworkCore --version 8.0.0

dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 8.0.0

dotnet add package Microsoft.EntityFrameworkCore.Tools --version 8.0.0

Issue 3: Database already exists

Scenario: You want to start fresh

Solution: Delete and recreate the database:


// Add this before EnsureCreated()

db.Database.EnsureDeleted();  // Deletes the database if it exists

db.Database.EnsureCreated();   // Creates a fresh database

Issue 4: Nullable reference types warnings

Warning: Non-nullable property 'Title' must contain a non-null value when exiting constructor

Solution: Either make it nullable (string?) or initialize it:


public string Title { get; set; } = string.Empty;


2.11 Best Practices Introduced

  1. Use folders to organize code: Models folder for entities, Data folder for DbContext.
  2. Always dispose DbContext: Use using statement or using var to ensure connections are closed.
  3. Log SQL during development: optionsBuilder.LogTo(Console.WriteLine) helps you understand what EF Core is doing.
  4. Use data annotations for simple validation: [Required], [MaxLength] provide both validation and database schema hints.
  5. Start with EnsureCreated for learning: But remember we'll replace it with Migrations in the next chapter.

2.12 Chapter Summary

Concept What We Learned
Project Setup Create a console app and install EF Core NuGet packages (Core, SqlServer, Tools)
Entity Class Plain C# class with properties. Id becomes primary key. Data annotations add database constraints.
DbContext Main class that coordinates database operations. Contains DbSet properties for tables.
Connection String Tells EF Core which database server and database to use.
EnsureCreated Quick way to create database (development only, not for production).
Add and SaveChanges Add tracks entities, SaveChanges executes SQL and returns number of affected rows.
Querying Using LINQ on DbSet to retrieve data (ToList, OrderBy).

What's Next?

In Chapter 3: Database Migrations, we will:

  • Understand why EnsureCreated() is not suitable for production
  • Learn what migrations are and why they're essential
  • Install the dotnet-ef global tool
  • Create our first migration
  • Apply migrations to update the database schema
  • Add a new property to our Book entity and create a second migration
  • Generate SQL scripts for production deployments

Migrations are what make EF Core truly professional - they give you version control for your database schema!


Post a Comment

Feel free to ask your query...
Cookie Consent
We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.
Oops!
It seems there is something wrong with your internet connection. Please connect to the internet and start browsing again.
AdBlock Detected!
We have detected that you are using adblocking plugin in your browser.
The revenue we earn by the advertisements is used to manage this website, we request you to whitelist our website in your adblocking plugin.
Site is Blocked
Sorry! This site is not available in your country.