Welcome to the EF Core Workshop! This guide will walk you through setting up a small database-driven app using Entity Framework Core in a .NET console application.
In software development, we often need to store and retrieve data from a database. Instead of writing raw SQL queries everywhere in our code, we can use an Object-Relational Mapper (ORM).
An ORM helps bridge the gap between:
- 🧱 Relational Databases (like SQL Server)
- 💻 Object-Oriented Code (like your C# classes)
Entity Framework Core (EF Core) is Microsoft’s modern, lightweight ORM for .NET. It allows you to:
- Define your database schema using C# classes
- Use LINQ to query data
- Migrate schema changes without manual SQL
- Work across many database systems (SQL Server, SQLite, PostgreSQL, etc.)
With EF Core, your data logic is centralized, strongly typed, and easier to maintain.
- Model entities with relationships
- Connect to SQL Server
- Create and apply migrations
- Seed and query data effectively
- Use LINQ for data access
- Perform CRUD operations
- Handle relationships (one-to-many, many-to-many)
- tested on .net 9.0.3
- Visual Studio 2022 or vscode
- Workshop sql server instance (or local SQL Server Express)
- SQL Server Management Studio (SSMS) or Azure Data Studio
- dotnet CLI installed
- EF Core CLI tools installed
- SQL Server Management Studio (SSMS) or Azure Data Studio
- Visual Studio Code (optional)
Add a unique name for your app:
dotnet new console -n EfDemoApp
cd EfDemoApp
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Configuration.FileExtensions
Add the file to the root of the project directory. This file will hold your connection string and other configuration settings.
Configure your connection string in appsettings.json
:
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=EfDemoDb;Trusted_Connection=True;TrustServerCertificate=True;"
}
}
📌 Right-click the file in VS and set Copy to Output Directory: Copy if newer
Create a Models
folder and add three classes:
public class Customer
{
public int Id { get; set; }
public required string Name { get; set; }
public ICollection<Order> Orders { get; set; } = new List<Order>();
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; } = null!;
public ICollection<OrderLine> OrderLines { get; set; } = new List<OrderLine>();
}
public class OrderLine
{
public int Id { get; set; }
public int OrderId { get; set; }
public required string ProductName { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public Order Order { get; set; } = null!;
}
Add a Data
folder and create AppDbContext.cs
:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Customer> Customers => Set<Customer>();
public DbSet<Order> Orders => Set<Order>();
public DbSet<OrderLine> OrderLines => Set<OrderLine>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Customer>().HasData(
new Customer { Id = 1, Name = "Alice" },
new Customer { Id = 2, Name = "Bob" }
);
modelBuilder.Entity<Order>().HasData(
new Order { Id = 1, CustomerId = 1 },
new Order { Id = 2, CustomerId = 2 }
);
modelBuilder.Entity<OrderLine>().HasData(
new OrderLine { Id = 1, OrderId = 1, ProductName = "Apples", Quantity = 2, Price = 10.5m },
new OrderLine { Id = 2, OrderId = 2, ProductName = "Bananas", Quantity = 5, Price = 3.2m }
);
}
}
Also add the AppDbContextFactory class to help with migrations in the Data
folder.
public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>();
optionsBuilder.UseSqlServer(config.GetConnectionString("DefaultConnection"));
return new AppDbContext(optionsBuilder.Options);
}
}
dotnet ef migrations add InitialCreate
dotnet ef database update
using EfDemoApp.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString = config.GetConnectionString("DefaultConnection");
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer(connectionString)
.Options;
using var context = new AppDbContext(options);
{
Console.WriteLine($"Customers: {context.Customers.Count()}");
Console.WriteLine($"Orders: {context.Orders.Count()}");
Console.WriteLine($"OrderLines: {context.OrderLines.Count()}");
}
-
Add a phone number to Customer
- Modify the model
- Create a new migration
- Update the database
-
Add a new table: Product
- Fields:
Id
,Name
,Price
- Link
OrderLine
toProduct
(one-to-many) - Seed a few products
- Fields:
-
Write code to insert new Customers, Orders, OrderLines, and Products
-
Query data using LINQ
- Get all orders with customer names and order totals
- Group orders by customer
- Use joins and projections
-
Add a many-to-many relationship
- Create a new
OrderProduct
entity to linkOrder
andProduct
- Update the models and context
- Seed data for this relationship
- Write LINQ queries to retrieve orders with products
- Instructions for many-to-many
- Create a new
Happy coding! 🎉