Interact With an in-memory Database Using Entity Framework Core
A glimpse into the power of ORM with .NET
An object-relational mapper (ORM) is a program that acts as a bridge between your application and database. Entity Framework Core is Microsoft’s implementation of an ORM.
With EF Core, you don’t have to worry about underlying database code; rather, you can insert data right from your .NET application!
Today, we’ll create an in-memory database and perform CRUD operations in it with the help of Entity Framework Core.
So here’s the idea. We’ll create a coffee management system where user can choose some coffee, quantity, price, etc.
Let’s think for a moment how the project structure should go.
First, each coffee has some unique properties (name, size, price, etc.). So we have to create a blueprint class for these properties (Model.cs).
Second, we have to store what coffees (lattes, cappuccinos, etc.) we have in our database. This will be the data class (Data.cs).
Finally, we’ll interact with the database using EF Core and serve users what they ordered from the frontend (Program.cs).
For this project, we’ll import the Entity Framework Core and Swagger packages. Let’s get to work!
A Simple Coffee Management API with EF Core
Create a new directory and open it in VS Code. In VS Code’s terminal, type the following:
dotnet new webThis will create the necessary setup needed for building a minimal API — a Microsoft implementation of a REST API.
Run the application for once to check if everything is working properly. You should see a “Hello World!” message on your localhost.
Package Installation
Enter the following command in VS Code’s terminal. These will install EF Core and Swagger packages one by one.
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
dotnet add package NSwag.AspNetCore
dotnet add package Swashbuckle.AspNetCoreYou can verify the installation by typing the following command; you should see 4 package names.
dotnet list packageBuilding the Blueprint
Create the Model.cs file and open it.
Like I said, we need to define what properties we want our coffees to have. This is where we do so.
Create a public class with public properties and a getter, a setter, and lastly, the constructor to auto-assign values.
namespace CoffeeModel
{
public class Coffee
{
public int Id { get; set; }
public string? Name { get; set; }
public char Size { get; set; }
public int Quantity { get; set; }
public double Price { get; set; }
public Coffee(int Id, string Name, char Size, int Quantity, double Price)
{
this.Id = Id;
this.Name = Name;
this.Size = Size;
this.Quantity = Quantity;
this.Price = Price;
}
}
}Establishing connection with database
Next, we have to store the actual information of coffees. We store them in a database. And we talk to that database through Entity Framework Core.
Here’s a brief overview.
The class that’ll talk to the database is called the context class. This class must be derived from DbContext. This inheritance is crucial to establish a connection with the database. It’ll act as the manager to pass data back and forth.
The classes inside this context class must have the type of DbSet<T>. When you do so, these classes are called entities. These entities will later become a table in the database with columns for each of their properties.
To recap: ContextClass > DbContext > DbSet > Blueprint Classes > Tables
I hope it makes sense. If it does not, read again.
Get Forhad Rahman’s stories in your inbox
Join Medium for free to get updates from this writer.
So in our case, we’ll create a CoffeeData class with Coffee as the DbSet property in Data. cs.
using CoffeeModel;
using Microsoft.EntityFrameworkCore;
namespace CoffeeData
{
public class CoffeeDb : DbContext
{
public DbSet<Coffee> Coffees { get; set; }
}
}What this code is saying is, “Create a database named CoffeeDb and set columns found in the Coffee class’s properties (Id, Name, Quantity, etc.).”
Setting up business logic
Now go to Program.cs, and replace everything with the following code:
using Microsoft.EntityFrameworkCore;
using CoffeeData;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<CoffeeDb>(opt => opt.UseInMemoryDatabase("CoffeeList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
app.MapGet("/", () => "Welcome to Coffee Management System [EF Core Edition]!");
app.Run();The barebones of the app are mostly ready. Now we can set up Swagger to start testing the endpoints. Swagger is a set of tools for an API testing environment. The most famous one is Postman. But NSwag here is fine for now.
Run this in VS Code terminal:
dotnet add package NSwag.AspNetCoreNow add these before the var app = builder. Build(); code:
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApiDocument(config =>
{
config.DocumentName = "TodoAPI";
config.Title = "TodoAPI v1";
config.Version = "v1";
});And add this code after the var app = builder. Build(); code:
if (app.Environment.IsDevelopment())
{
app.UseOpenApi();
app.UseSwaggerUi(config =>
{
config.DocumentTitle = "TodoAPI";
config.Path = "/swagger";
config.DocumentPath = "/swagger/{documentName}/swagger.json";
config.DocExpansion = "list";
});
}Basically, they allow you to test your API on localhost.
Run the app. You should see localhost opening in the browser with the “Welcome to Coffee Management System [EF Core Edition]!” text.
Let’s add some CRUD operations to make the app more practical for our customers.
Retrieve coffee from the database.
This code can retrieve all coffees and one coffee at a time. So, in the /coffee endpoint, we’re creating an instance of CoffeeDb. While it’s selected, we’re picking the database table named Coffees and listing all the elements of it.
In the second case, we’re selecting an endpoint by a specific ID, then searching that ID in the database. If we find the ID, we return status 200; status 404, not found, otherwise.
// Retrieve all coffees
app.MapGet("/coffee", (CoffeeDb db) =>
{
return db.Coffees.ToList();
});
// Retrieve one coffee
app.MapGet("/coffee/{id}", async (int id, CoffeeDb db) =>
await db.Coffees.FindAsync(id)
is Coffee coffee
? Results.Ok(coffee)
: Results.NotFound());This code adds one coffee to the database. In the /coffee/add endpoint, we add a new coffee in the database, save the changes, and return the result.
// Add a coffee
app.MapPost("/coffee/add", (Coffee coffee, CoffeeDb db) =>
{
db.Coffees.Add(coffee);
db.SaveChanges();
return Results.Created($"/coffees/{coffee.Id}", coffee);
});This code makes changes to an existing item. We first store the correct entry to a variable. Then we make the changes one by one and call the SaveChanges method.
// Update a coffee
app.MapPut("/coffee/update/{id}", (int id, Coffee coffee, CoffeeDb db) =>
{
var newCoffee = db.Coffees.Find(id);
if (newCoffee is null) return Results.NotFound();
newCoffee.Name = coffee.Name;
newCoffee.Size = coffee.Size;
newCoffee.Quantity = coffee.Quantity;
newCoffee.Price = coffee.Price;
db.SaveChanges();
return Results.NoContent();
});This code deletes an entry. As usual, we first store the value in a variable and ensure we actually got the correct entry. Then we remove it from the coffees table, save the change, and return the result.
// Delete a coffee
app.MapDelete("/coffee/delete/{id}", (int id, CoffeeDb db) =>
{
var deleteCoffee = db.Coffees.Find(id);
if (deleteCoffee is null) return Results.NotFound();
db.Coffees.Remove(deleteCoffee);
db.SaveChanges();
return Results.NoContent();
});Lastly, run the app using the Run method. You can explicitly mention which port you want to use.
app.Run("http://localhost:5000");Swagger
After running the code, you should see the Swagger UI at the address “http://localhost:5000/swagger/.”
Now we can add items, check for those items, and modify them. We can also check with an ID.
The delete method is also doing fine.
Perfect!
Now this data is ready to serve in a frontend… almost. We’ll look into that another day.
Here’s the full code:
using Microsoft.EntityFrameworkCore;
using CoffeeData;
using CoffeeModel;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<CoffeeDb>(opt => opt.UseInMemoryDatabase("CoffeeList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApiDocument(config =>
{
config.DocumentName = "TodoAPI";
config.Title = "TodoAPI v1";
config.Version = "v1";
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseOpenApi();
app.UseSwaggerUi(config =>
{
config.DocumentTitle = "TodoAPI";
config.Path = "/swagger";
config.DocumentPath = "/swagger/{documentName}/swagger.json";
config.DocExpansion = "list";
});
}
// ----------------------------- CRUD ----------------------------- //
app.MapGet("/", () => "Welcome to Coffee Management System [EF Core Edition]!");
// Retrieve all coffees
app.MapGet("/coffee", (CoffeeDb db) =>
{
return db.Coffees.ToList();
});
// Retrieve one coffee
app.MapGet("/coffee/{id}", async (int id, CoffeeDb db) =>
await db.Coffees.FindAsync(id)
is Coffee coffee
? Results.Ok(coffee)
: Results.NotFound());
// Add a coffee
app.MapPost("/coffee/add", (Coffee coffee, CoffeeDb db) =>
{
db.Coffees.Add(coffee);
db.SaveChanges();
return Results.Created($"/coffees/{coffee.Id}", coffee);
});
// Update a coffee
app.MapPut("/coffee/update/{id}", (int id, Coffee coffee, CoffeeDb db) =>
{
var newCoffee = db.Coffees.Find(id);
if (newCoffee is null) return Results.NotFound();
newCoffee.Name = coffee.Name;
newCoffee.Size = coffee.Size;
newCoffee.Quantity = coffee.Quantity;
newCoffee.Price = coffee.Price;
db.SaveChanges();
return Results.NoContent();
});
// Delete a coffee
app.MapDelete("/coffee/delete/{id}", (int id, CoffeeDb db) =>
{
var deleteCoffee = db.Coffees.Find(id);
if (deleteCoffee is null) return Results.NotFound();
db.Coffees.Remove(deleteCoffee);
db.SaveChanges();
return Results.NoContent();
});
app.Run("http://localhost:5000");That’s it for today. Hope this helps. Thanks to MS docs, Claude, and a bunch of random websites that helped me understand this topic.
Cheers!
Note: This post is a personal reference while learning backend development. Some information may be inaccurate or incomplete. Please research yourself as well.








