Use Entity Framework Core In-Memory Database in ASP.NET Core

Most of the production cases call for storing data in some physical data
store such as a SQL Server database. However, for testing and demoing purposes
you may want to store data in memory rather than persistent store. That's where
Entity Framework Core InMemory provider can be helpful. In this article we will
examine the usage of the EF Core InMemory provider in an ASP.NET Core Razor
Pages application.
To get started, create a new ASP.NET Core web application using empty project
template. You could have also use Razor Pages project template but we will use
empty project to skip the default project items that get added.

Then add NuGet package for the EF Core InMemory data provider.

Next, add a folder named DataAccess in the project root and add two class
files namely Customer.cs and AppDbContext.cs.
The Customer class is EF Core entity class and looks like this:
public class Customer
{
public string CustomerID { get; set; }
public string CustomerName { get; set; }
}
The AppDbContext class is a custom DbContext class and looks like this:
public class AppDbContext:DbContext
{
public AppDbContext(DbContextOptions<AppDbContext>
options):base(options)
{
}
public DbSet<Customer> Customers { get; set; }
}
Notice that the AppDbContext class inherits from DbContext base class and
contains Customers DbSet.
Now open Program.cs and register the AppDbContext with the DI container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<AppDbContext>
(o => o.UseInMemoryDatabase("MyDatabase"));
var app = builder.Build();
app.MapRazorPages();
app.Run();
Note that we registered Razor Pages related services using AddRazorPages()
and AppDbContext using AddDbContext() call. We have also called MapRazorPages()
to configure Razor Pages routing.
Also note the use of UseInMemoryDatabase() extension method that specifies
that an in-memory database with the specified name (MyDatabase) is to be used.
Our AppDbContext is ready to be injected in the other parts of the
application. But we will also seed a few sample Customer entries into the
in-memory database. To do this we will write a helper method as shown below.
static void AddCustomerData(WebApplication app)
{
var scope = app.Services.CreateScope();
var db = scope.ServiceProvider.GetService<AppDbContext>();
var customer1 = new Customer
{
CustomerID = "Customer ID 1",
CustomerName = "Customer Name 1"
};
var customer2 = new Customer
{
CustomerID = "Customer ID 2",
CustomerName = "Customer Name 2"
};
var customer3 = new Customer
{
CustomerID = "Customer ID 3",
CustomerName = "Customer Name 3"
};
db.Customers.Add(customer1);
db.Customers.Add(customer2);
db.Customers.Add(customer3);
db.SaveChanges();
}
The AddCustomerData() method should be placed immediately after the app.Run()
call.
The AddCustomerData() method accepts a WebApplication parameter. Inside, it
retrieves an instance of AppDbContext from the DI container. This is done using
the CreateScope() and the GetService() methods.
Then we create three Customer entities and add them to the Customers DbSet.
Finally, we call SaveChanges() to save the data.
We will call the AddCustomerData() method below the builder.Build() line.
...
...
var app = builder.Build();
AddCustomerData(app);
app.MapRazorPages();
app.Run();
...
...
Now that we seeded some test data, let's create a Razor Page that displays
and modifies this data.
Add a new folder named Pages in the project root and add a Razor Page named
Index.cshtml in it using Add New Item dialog.

Open the IndexModel page model class and add the following code to it.
public class IndexModel : PageModel
{
public readonly AppDbContext db;
public List<Customer> Customers { get; set; }
public IndexModel(AppDbContext db)
{
this.db = db;
}
public void OnGet()
{
Customers = db.Customers.ToList();
}
public void OnPostUpdate()
{
Customers = db.Customers.ToList();
foreach (var item in Customers)
{
item.CustomerName += " Changed";
}
db.SaveChanges();
}
}
We added a variable for storing the injected AppDbContext. We also added
Customers property that stores a List of Customer entities.
The constructor of the class accepts the injected AppDbContext and stores it
in the db local variable.
The OnGet() page handler is called when the page is initially loaded in the
browser. It simply retrieves a List of Customer object using the ToList() method
and stores it in the Customers property.
The OnPostUpdate() page handler is called when a POST request is made to the
page as a part of form submission (more on that shortly). Inside, we fetch the
Customers data again and modify CustomerName of each Customer entity. This is
done just to check whether the in-memory database remembers our changes
successfully.
Now open the Index.cshtml file and add the following markup in it:
@page
@model InMemoryDbDemo.Pages.IndexModel
@addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers
<h1>List of Customers</h1>
<table border="1" cellpadding="10">
<tr>
<th>Customer ID</th>
<th>Customer Name</th>
</tr>
@if (Model.Customers != null)
{
@foreach (var item in Model.Customers)
{
<tr>
<td>@item.CustomerID</td>
<td>@item.CustomerName</td>
</tr>
}
}
</table>
<br /><br />
<form method="post">
<button type="submit" asp-page-handler="Update">
Update Customers
</button>
</form>
Notice the code shown in bold letters. We enable tag helpers for this page
using @addTagHelper directive.
We render a list of customers in a table. Before rendering individual rows,
we check if Model.Customers property is null or not. If it has some data we run
a foreach loop and render table rows.
At the bottom of the page we have a form with a submit button - Update
Customers. The asp-page-handler attribute of the button element is set to Update
indicating that our OnPostUpdate() page handler should be called upon the form
submission.
Save all the files and run the application. If all goes well you will see a
table as shown below.

Now click on the Update Customers button and see whether the changes are
reflected on the UI as shown below.

If you stop the application and run it again you will find the original data
(that we seeded in Program.cs) again indicating that the changes are in-memory
and non persistent between multiple application runs.
You may read more about EF Core InMemory provider in the official
documentation
here. In the next article we will learn to use SQLite in-memory database
instead of EF Core InMemory provider.
That's it for now! Keep coding!!