Published:

.NET 6.0 - Connect to InMemory Database with Entity Framework Core

Tutorial built with .NET 6.0

This post shows how to connect a .NET 6 API to an InMemory database for testing with Entity Framework Core.

The example code is from of a .NET 6 CRUD API tutorial I posted recently that uses the EF Core InMemory db provider. For full details about the .NET CRUD API see .NET 6.0 - CRUD API Example and Tutorial.

When the API is started it automatically creates a database in memory so the API can be tested without needing a real database like SQL Server, MySQL, PostgreSQL etc. It's only meant for testing because the in memory database is destroyed when the API is stopped.


Tutorial Contents


Tools required for this tutorial

To follow the steps in this tutorial you'll need the following:

  • .NET SDK - includes the .NET runtime and command line tools.
  • Visual Studio Code - code editor that runs on Windows, Mac and Linux. If you have a different preferred code editor that's fine too.
  • C# extension for Visual Studio Code - adds support to VS Code for developing .NET applications.


Download & Run the example .NET API

Follow these steps to download and run the .NET 6 CRUD API on your local machine with the default EF Core InMemory database:

  1. Download or clone the tutorial project code from https://github.com/cornflourblue/dotnet-6-crud-api
  2. Start the api by running dotnet run from the command line in the project root folder (where the WebApi.csproj file is located), you should see the message Now listening on: http://localhost:4000.
  3. You can test the API directly with a tool such as Postman or hook it up with the example Angular or React app available.

Starting in debug mode

You can also start the application in debug mode in VS Code by opening the project root folder in VS Code and pressing F5 or by selecting Debug -> Start Debugging from the top menu, running in debug mode allows you to attach breakpoints to pause execution and step through the application code. For detailed instructions including a short demo video see VS Code + .NET - Debug a .NET Web App in Visual Studio Code.


.NET Code to Connect to InMemory Database

Below are the files responsible for connecting to the in memory database and performing CRUD operations.


.NET 6 Web Api csproj

Path: /WebApi.csproj

The csproj (C# project) is an MSBuild based file that contains target framework and NuGet package dependency information for the application.

The package required to use the EF Core InMemory database provider is Microsoft.EntityFrameworkCore.InMemory. The provider is configured in the data context.

For more info on the C# project file see .NET + MSBuild - C# Project File (.csproj) in a Nutshell.

<Project Sdk="Microsoft.NET.Sdk.Web">
    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="AutoMapper" Version="11.0.1" />
        <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
        <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.3" />
    </ItemGroup>
</Project>


Data Context

Path: /Helpers/DataContext.cs

The data context class is used for accessing application data through Entity Framework. It derives from the Entity Framework DbContext class and has a public Users property for accessing and managing user data. The data context is used by the user service for handling all low level data (CRUD) operations.

options.UseInMemoryDatabase() configures Entity Framework to create and connect to an in-memory database so the API can be tested without a real database.

namespace WebApi.Helpers;

using Microsoft.EntityFrameworkCore;
using WebApi.Entities;

public class DataContext : DbContext
{
    protected readonly IConfiguration Configuration;

    public DataContext(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        // in memory database used for simplicity, change to a real db for production applications
        options.UseInMemoryDatabase("TestDb");
    }

    public DbSet<User> Users { get; set; }
}


User Service

Path: /Services/UserService.cs

The user service is responsible for all database interaction and core business logic related to user CRUD operations.

The top of the file contains an interface that defines the user service, just below that is the concrete user service class that implements the interface and interacts with the EF Core InMemory database.

namespace WebApi.Services;

using AutoMapper;
using BCrypt.Net;
using WebApi.Entities;
using WebApi.Helpers;
using WebApi.Models.Users;

public interface IUserService
{
    IEnumerable<User> GetAll();
    User GetById(int id);
    void Create(CreateRequest model);
    void Update(int id, UpdateRequest model);
    void Delete(int id);
}

public class UserService : IUserService
{
    private DataContext _context;
    private readonly IMapper _mapper;

    public UserService(
        DataContext context,
        IMapper mapper)
    {
        _context = context;
        _mapper = mapper;
    }

    public IEnumerable<User> GetAll()
    {
        return _context.Users;
    }

    public User GetById(int id)
    {
        return getUser(id);
    }

    public void Create(CreateRequest model)
    {
        // validate
        if (_context.Users.Any(x => x.Email == model.Email))
            throw new AppException("User with the email '" + model.Email + "' already exists");

        // map model to new user object
        var user = _mapper.Map<User>(model);

        // hash password
        user.PasswordHash = BCrypt.HashPassword(model.Password);

        // save user
        _context.Users.Add(user);
        _context.SaveChanges();
    }

    public void Update(int id, UpdateRequest model)
    {
        var user = getUser(id);

        // validate
        if (model.Email != user.Email && _context.Users.Any(x => x.Email == model.Email))
            throw new AppException("User with the email '" + model.Email + "' already exists");

        // hash password if it was entered
        if (!string.IsNullOrEmpty(model.Password))
            user.PasswordHash = BCrypt.HashPassword(model.Password);

        // copy model to user and save
        _mapper.Map(model, user);
        _context.Users.Update(user);
        _context.SaveChanges();
    }

    public void Delete(int id)
    {
        var user = getUser(id);
        _context.Users.Remove(user);
        _context.SaveChanges();
    }

    // helper methods

    private User getUser(int id)
    {
        var user = _context.Users.Find(id);
        if (user == null) throw new KeyNotFoundException("User not found");
        return user;
    }
}


How to Connect to a Real Database

For instructions on how to update the CRUD API to connect to a real database see the below posts:

 


Subscribe or Follow Me For Updates

Subscribe to my YouTube channel or follow me on Twitter, Facebook or GitHub to be notified when I post new content.

Other than coding...

I'm currently attempting to travel around Australia by motorcycle with my wife Tina on a pair of Royal Enfield Himalayans. You can follow our adventures on YouTube, Instagram and Facebook.


Need Some .NET Help?

Search fiverr to find help quickly from experienced .NET developers.



Supported by