Exercise - Add EF Core to minimal API

Completed

You're a developer for a company, and you and your company have heard about the new minimal API. Your manager has asked you to create a project for it so that you can discuss whether to use it on your next project.

Note

This module uses the .NET CLI (Command Line Interface) and Visual Studio Code for local development. After completing this module, you can apply the concepts using Visual Studio (Windows), Visual Studio for Mac (macOS), or continued development using Visual Studio Code (Windows, Linux, & macOS).

This module uses the .NET 8.0 SDK. Ensure that you have .NET 8.0 installed by running the following command in your preferred command terminal:

dotnet --list-sdks

Output similar to the following example appears:

6.0.317 [C:\Program Files\dotnet\sdk]
7.0.401 [C:\Program Files\dotnet\sdk]
8.0.100 [C:\Program Files\dotnet\sdk]

Ensure that a version that starts with 8 is listed. If none is listed or the command isn't found, install the most recent .NET 8.0 SDK.

Set up the project

First, you need to create a project. You've installed .NET 6 and you're ready to go. In this unit, you'll add data persistence to a pizza management API.

  1. In a terminal, create a web API by running dotnet new:

    dotnet new web -o PizzaStore -f net8.0
    

    You should see the PizzaStore directory.

  2. Go to the PizzaStore directory by entering the following command:

    cd PizzaStore
    
  3. Install the Swashbuckle package:

    dotnet add package Swashbuckle.AspNetCore --version 6.5.0
    
  4. Open the project in Visual Studio Code.

  5. Using Visual Studio Code, create a Pizza.cs file in the project root and give it the following content:

    namespace PizzaStore.Models 
    {
        public class Pizza
        {
              public int Id { get; set; }
              public string? Name { get; set; }
              public string? Description { get; set; }
        }
    }
    

    The preceding Pizza class is a simple object that represents a pizza. This code is your data model. Later, you'll use Entity Framework (EF) Core to map this data model to a database table.

  6. Open Program.cs and add the highlighted code:

    using Microsoft.OpenApi.Models;
    
    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen(c =>
    {
         c.SwaggerDoc("v1", new OpenApiInfo {
             Title = "PizzaStore API",
             Description = "Making the Pizzas you love",
             Version = "v1" });
    });
    
    var app = builder.Build();
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
       c.SwaggerEndpoint("/swagger/v1/swagger.json", "PizzaStore API V1");
    });
    
    app.MapGet("/", () => "Hello World!");
    
    app.Run();
    

    You might receive a prompt from Visual Studio Code to add assets to debug the project. Select Yes in the dialog.

Add EF Core to the project

To store the items in the to-do list, install the EntityFrameworkCore.InMemory package.

  1. Press Ctrl+` to open a terminal in Visual Studio Code. In the new terminal, enter the following code to add the EF Core InMemory package:

    dotnet add package Microsoft.EntityFrameworkCore.InMemory --version 8.0
    
  2. Add using Microsoft.EntityFrameworkCore; to the top of your Program.cs and Pizza.cs files.

    Now that you have EF Core added to the project, you can wire up your code to the data you want to save and query it. To do this step, you create a PizzaDb class. The PizzaDb class will do the following tasks:

    • Expose your Pizzas property from your list of Pizza in the database.
    • Use UseInMemoryDatabase to wire the in-memory database storage. Your data is stored here as long as the app is running.
  3. To set up your in-memory database, add the following code to the bottom of the Pizza.cs file (above the final }). You'll have two class definitions within the PizzaStore.Models namespace.

    class PizzaDb : DbContext
    {
        public PizzaDb(DbContextOptions options) : base(options) { }
        public DbSet<Pizza> Pizzas { get; set; } = null!;
    }
    

    DbContext represents a connection or session that's used to query and save instances of entities in a database.

  4. Add using PizzaStore.Models; to the top of your Program.cs file.

  5. In Program.cs, before the call to AddSwaggerGen, add the following code:

    builder.Services.AddDbContext<PizzaDb>(options => options.UseInMemoryDatabase("items"));
    

Return a list of items

  • To read from a list of items in the pizza list, add the following code above the call to app.Run(); to add a "/pizzas" route:

    app.MapGet("/pizzas", async (PizzaDb db) => await db.Pizzas.ToListAsync());
    

Run the application

  1. Make sure you've saved all your changes. Run the app by calling dotnet run in the terminal. This action will build the app and host it on a port from 5000-5300. HTTPS will have a port selected for it in the range 7000-7300.

    Note

    If you want to override the random port selection behavior, you can set the ports to use in launchSettings.json.

    dotnet run
    

    Here's what the output can look like in the terminal:

    Building...
     info: Microsoft.Hosting.Lifetime[14]
           Now listening on: https://localhost:7200
     info: Microsoft.Hosting.Lifetime[14]
           Now listening on: http://localhost:5100
     info: Microsoft.Hosting.Lifetime[0]
           Application started. Press Ctrl+C to shut down.
     info: Microsoft.Hosting.Lifetime[0]
           Hosting environment: Development
     info: Microsoft.Hosting.Lifetime[0]
           Content root path: /<path>/PizzaStore
    
  2. In your browser, go to https://localhost:{PORT}/swagger. Select the GET /pizzas button, followed by Try it out and Execute. You'll see that the list is empty under Response body.

  3. In the terminal, press Ctrl+C to stop running the program.

Create new items

Let's add the code to POST new items to the pizzas list. In Program.cs, add the following code under the app.MapGet that you created earlier.

app.MapPost("/pizza", async (PizzaDb db, Pizza pizza) =>
{
    await db.Pizzas.AddAsync(pizza);
    await db.SaveChangesAsync();
    return Results.Created($"/pizza/{pizza.Id}", pizza);
});

Test the API

Make sure you've saved all your changes and run the app again. Go back to the Swagger UI and now you should see POST/pizza. To add new items to the pizza list:

  1. Select POST /pizza.

  2. Select Try it out.

  3. Replace the request body with the following JSON:

    {
        "name": "Pepperoni",
        "description": "A classic pepperoni pizza"
    }
    
  4. Select Execute.

To read the items in the list:

  1. Select GET /pizzas.

  2. Select Try it out.

  3. Select Execute.

    The Response body will include the items just added.

    [
      {
        "id": 1,
        "name": "Pepperoni",
        "description": "A classic pepperoni pizza"
      }
    ]
    
  4. Press Ctrl+C in the terminal to stop running the app. For the rest of this exercise, stop and restart the app as desired to test your changes. Be sure to save all your changes before you dotnet run!

Get a single item

To GET an item by id, add the code under the app.MapPost route you created earlier.

app.MapGet("/pizza/{id}", async (PizzaDb db, int id) => await db.Pizzas.FindAsync(id));

Test GET by ID

To test this operation, you can either go to https://localhost:{PORT}/pizza/1 or use the Swagger UI. Since you're using an in-memory database, the pizza you previously created won't be listed if you've restarted the application. So, you'll need to use your POST operation to add it again.

Update an item

To update an existing item, add the code under the GET /pizza/{id} route you created:

app.MapPut("/pizza/{id}", async (PizzaDb db, Pizza updatepizza, int id) =>
{
      var pizza = await db.Pizzas.FindAsync(id);
      if (pizza is null) return Results.NotFound();
      pizza.Name = updatepizza.Name;
      pizza.Description = updatepizza.Description;
      await db.SaveChangesAsync();
      return Results.NoContent();
});

Test PUT

  1. Select PUT /pizza/{id} in the Swagger UI.

  2. Select Try it out.

  3. In the id text box, enter 1.

  4. Finally, update Request body. Paste the following JSON and change name to Pineapple.

    {
       "id": 1,
       "name": "Pineapple"
    }
    
  5. Select Execute.

To test the code, scroll back to GET /pizza/{id}. The pizza now has the name Pineapple.

Delete an item

To delete an existing item, add the code under PUT /pizza/{id} that you created earlier:

app.MapDelete("/pizza/{id}", async (PizzaDb db, int id) =>
{
   var pizza = await db.Pizzas.FindAsync(id);
   if (pizza is null)
   {
      return Results.NotFound();
   }
   db.Pizzas.Remove(pizza);
   await db.SaveChangesAsync();
   return Results.Ok();
});

Testing DELETE

Now try deleting an item by using the Swagger interface.

In this unit, you added EF Core to an existing minimal API application and used an in-memory database to store the data. Next, you'll learn how to use a real database to store the data so that it persists between application shutdowns.