One of the most exciting features in the new version of Entity Framework (Entity Framework Core 1.0) is the addition of an in-memory data provider. This makes it possible to prototype applications and write tests without having to set up a local or external database. When you’re ready to switch to using a real database, you can simply swap in your actual provider.
In this tutorial, I’ll show you how to set up the Entity Framework Core InMemory provider and use it to prototype a simple ASP.NET Core API. If you’ve never set up ASP.NET Core before, follow my earlier tutorial on creating a new ASP.NET Core project to create a new API application called PrototypeApi
.
Setting up Entity Framework Core
First, add the Microsoft.EntityFrameworkCore.InMemory
package to your project. In Visual Studio, you can use the NuGet Package Manager to do this (right-click on the project and choose Manage NuGet Packages). Alternatively, you can edit the project.json file and add this line to the dependencies
section:
1 2 |
"Microsoft.EntityFrameworkCore.InMemory": "1.0.0" |
Use dotnet restore
to install the package if you aren’t using Visual Studio.
The API I’ll be demonstrating contains a collection of users and a collection of posts. Create some model classes to represent these entities (in a new folder called DbModels):
DbModels/User.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using System.Collections.Generic; namespace PrototypeApi.DbModels { public class User { public string Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public List<Post> Posts { get; set; } } } |
DbModels/Post.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using System; namespace PrototypeApi.DbModels { public class Post { public string Id { get; set; } public string UserId { get; set; } public User User { get; set; } public string Content { get; set; } } } |
These simple classes will represent the data that will be saved and loaded from the database.
In this data model, there’s a straightforward one-to-many relationship between Users and Posts: a Post is “owned” by a User, and a User may have many Posts. The Posts
and User
properties will be automatically discovered and populated by Entity Framework Core.
You’ll also need to create a DbContext
that represents the connection to the database:
ApiContext.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using Microsoft.EntityFrameworkCore; using PrototypeApi.DbModels; namespace PrototypeApi { public class ApiContext : DbContext { public ApiContext(DbContextOptions<ApiContext> options) : base(options) { } public DbSet<User> Users { get; set; } public DbSet<Post> Posts { get; set; } } } |
You’re all set to connect this database context to your application using the in-memory data provider. In your Startup class’ ConfigureServices
method, add a new line:
1 2 3 4 5 6 7 |
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApiContext>(opt => opt.UseInMemoryDatabase()); services.AddMvc(); } |
At the top of the Startup class, add this line:
1 2 |
using Microsoft.EntityFrameworkCore; |
Seeding With Test Data on Startup
To add data to the in-memory database when the application starts up, add some code to the Configure
method:
1 2 3 4 5 6 7 8 9 10 11 |
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); var context = app.ApplicationServices.GetService<ApiContext>(); AddTestData(context); app.UseMvc(); } |
The new AddTestData
method looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
private static void AddTestData(ApiContext context) { var testUser1 = new DbModels.User { Id = "abc123", FirstName = "Luke", LastName = "Skywalker" }; context.Users.Add(testUser1); var testPost1 = new DbModels.Post { Id = "def234", UserId = testUser1.Id, Content = "What a piece of junk!" }; context.Posts.Add(testPost1); context.SaveChanges(); } |
Retrieving the Data
Querying and retrieving data from an in-memory provider use the same methods as any Entity Framework Core provider. To demonstrate retrieving the Users and Posts stored in the prototype database, create a simple controller:
Controllers/UsersController.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace PrototypeApi.Controllers { [Route("api/[controller]")] public class UsersController : Controller { private readonly ApiContext _context; public UsersController(ApiContext context) { _context = context; } public async Task<IActionResult> Get() { var users = await _context.Users .Include(u => u.Posts) .ToArrayAsync(); var response = users.Select(u => new { firstName = u.FirstName, lastName = u.LastName, posts = u.Posts.Select(p => p.Content) }); return Ok(response); } } } |
This does two things: it requests an ApiContext
object from the dependency injection service (using constructor injection) and exposes an HTTP GET route that returns all the users (and their posts) currently stored in the database.
In the Get
method, the Include
method explicitly tells Entity Framework Core to load the User’s Posts along with their other details. Entity Framework Core is smart enough to understand that the UserId
field on the Post
model represents a foreign key relationship between Users and Posts.
After the users and posts are asynchronously retrieved from the database, the array is projected (using Select
) into a response model that includes only the fields you need in the response. This keeps the JSON response minimal.
Next Steps
I’ve used the InMemory provider to rapidly prototype APIs and test ideas, and my favorite part is the ability to switch one line of code (AddDbContext
in the Startup
class) to connect to a live database like SQL Server.
This isn’t the only application of the InMemory provider, though. It’s also useful for building integration tests that need to exercise your data access layer or data-related business code. Instead of standing up a database for testing, you can run these integration tests entirely in memory.
Check out these resources to learn more about testing with InMemory, and ASP.NET Core:
If you have questions about using an in-memory database with Entity Framework Core, hit me up on Twitter @nbarbattini or leave a comment below!