Tutorial - Getting started with Entity Framework

Pirlouit Provis | May 8, 2023 min read

This tutorial will get you started on how you can add Entity Framework to your project. In this tutorial, I won’t explain every theoretical part of EF Core; I will focus on the practical part. If you want to learn more about EF Core, here is the official documentation.

We are going to build together a web API for a garage to keep track of their customers and the vehicles they own. We will use the code-first approach to create our tables.

Prerequisites

Create the solution

Create a new solution and name it EfTutorial. Then, create three projects within the solution:

  • WebAPI
  • Core
  • Infrastructure

create-new-project-img

configure-new-project-img

add-new-project-img

add-new-project-2-img

Your project architecture you look like this:

project-architecture-img

Add dependencies and references

Because we are using a clean architecture for this project, you must ensure projects reference the correct ones. Core will never reference any other project (exceptions exist but not for this tutorial). Infrastructure will reference Core, and WebAPI will reference both Core and Infrastructure.

To add references, right-click on Depedencies from the project and select Add Project Reference..., then select the project you want to reference.

project-reference-img

To add dependencies, right-click on the project and select Manage NuGet Packages..., then search for the package you want to install. You can also use the Package Manager Console and type Install-Package <package-name>.

manage-nuget-img add-nuget-img

Infrastructure

  • EfTutorial.Core
  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Sqlite
  • Microsoft.EntityFrameworkCore.Tools (for migrations using NuGet Package Manager Console)

WebAPI

  • EfTutorial.Core
  • EfTutorial.Infrastructure
  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Sqlite
  • Microsoft.EntityFrameworkCore.Design (required by Microsoft.EntityFrameworkCore.Tools)

Create the entities

Before creating the entities, we will create a base class for it so that we don’t have to always declare an ID. To do that, we will create a new folder called Entities in the Core project and add the following class named BaseEntity.cs:

namespace EfTutorial.Infrastructure.Data;

public abstract class BaseEntity
{
    public int Id { get; init; }
}

Once done, we can create our entities. Add the following classes to the same folder:

Vehicle

using EfTutorial.Infrastructure.Data;

namespace EfTutorial.Core.Entities;

public class Vehicle : BaseEntity
{
    public string Type { get; init; }
    public string Brand { get; init; }
    public string Model { get; init; }
    public int Year { get; init; }
    public string PlateNumber { get; init; }
    public string Fuel { get; init; }
    public int Power { get; init; }
}

Address

using EfTutorial.Infrastructure.Data;

namespace EfTutorial.Core.Entities;

public class Address : BaseEntity
{
    public string Street { get; init; }
    public string City { get; init; }
    public string ZipCode { get; init; }
    public string Country { get; init; }
}

Customer

using EfTutorial.Infrastructure.Data;

namespace EfTutorial.Core.Entities;

public class Customer : BaseEntity
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
    public DateTime Birthdate { get; init; }
    public string Email { get; init; }
    public string Phone { get; init; }
    public Address Address { get; init; }
    public List<Vehicle> Vehicles { get; init; }
}

Here, we are using init instead of set in our properties to ensure that our entities are immutable. However, you may be wondering why we don’t have properties for foreign keys. In Entity Framework Core 6, you don’t have to explicitly declare foreign keys in your entities (although you can if needed). EF Core will automatically create them and handle the relational mappings for you.

Create the DbContext

Create a new folder called Data in the Infrastructure project and add a new class named AppDbContext.cs:

using EfTutorial.Core.Entities;
using Microsoft.EntityFrameworkCore;

namespace EfTutorial.Infrastructure.Data;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }

    public DbSet<Customer> Customers { get; init; }
}

Configure EF with db and inject DbContext

Now, we need to configure the database and inject the DbContext into the IOC (Inversion of Control) container. To configure the database, we will use SQLite for simplicity, which will create a database in the root folder of the WebAPI project. To achieve this, add the following code to the Program.cs file:

using EfTutorial.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

// ...

builder.Services.AddDbContext<AppDbContext>(
    options => options.UseSqlite("Data Source=database.sqlite")
    // will create a SQL Lite DB in WebAPI project root folder
);

// ...

Create table from code first

We haven’t created any tables yet. As we are using the code-first approach, we need to use the Add-Migration <migration-name> command in the Package Manager Console. This command will generate a migration file in the Migrations folder within the Infrastructure project. The migration file will contain the necessary code to create the tables.

Make sure to select the Infrastructure project in the Package Manager Console.

Add Migration screenshot

After creating the migration, you can apply it by using the Update-Database command.

Update Database screenshot

Once you run the migration, you will see a new folder named Migrations created, which will contain your migration file.

Create a controller

To be able to add new customer and retrieve a list of all customers, we need to create a controller.

add-controller-img

This controller will contain two endpoints:

GET /customers to get all customers

[HttpGet]
public ActionResult<IEnumerable<Customer>> List()
{
    var customers = _dbContext.Customers
        .Include(c => c.Address) // To load the related Address
        .Include(c => c.Vehicles) // To load the related Vehicles
        .ToList();

    return Ok(customers);
}

Here, we need to use the Include method to load the related entities. If we don’t do that, the fields Address and Vehicles will be null.

POST /customers to add a new customer

[HttpPost]
public IActionResult Add([FromBody] Customer newCustomer)
{
    _dbContext.Customers.Add(newCustomer);
    _dbContext.SaveChanges();  // To save the changes to the database

    return Ok();
}

Your controller should look like this:

using EfTutorial.Core.Entities;
using EfTutorial.Infrastructure.Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace EfTutorial.WebAPI.Controllers;

[Route("api/[controller]")]
[ApiController]
public class CustomerController : ControllerBase
{
    private readonly AppDbContext _dbContext;

    public CustomerController(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    [HttpGet]
    public ActionResult<IEnumerable<Customer>> List()
    {
        var customers = _dbContext.Customers
            .Include(c => c.Address) // To load the related Address
            .Include(c => c.Vehicles) // To load the related Vehicles
            .ToList();

        return Ok(customers);
    }

    [HttpPost]
    public IActionResult Add([FromBody] Customer newCustomer)
    {
        _dbContext.Customers.Add(newCustomer);
        _dbContext.SaveChanges(); // To save the changes to the database

        return Ok();
    }
}

Run the application

You can now run the project and test the endpoint using the Swagger interface. You can try the POST endpoint with this sample:

{
  "FirstName": "John",
  "LastName": "Doe",
  "Birthdate": "1990-12-16",
  "Email": "johndoe@example.com",
  "Phone": "+1 123-456-7890",
  "Address": {
    "Street": "123 Main St",
    "City": "Anytown",
    "ZipCode": "12345",
    "Country": "United States"
  },
  "Vehicles": [
    {
      "Type": "Car",
      "Brand": "Toyota",
      "Model": "Camry",
      "Year": 2022,
      "PlateNumber": "ABC123",
      "Fuel": "Gasoline",
      "Power": 180
    },
    {
      "Type": "Motorcycle",
      "Brand": "Honda",
      "Model": "CBR500R",
      "Year": 2021,
      "PlateNumber": "DEF456",
      "Fuel": "Gasoline",
      "Power": 47
    }
  ]
}

swagger-1-img

If you list all the customers, you should see the one you just added.

swagger-2-img

Going further

This project is purpose to help you quickly understand how to add Entity Framework to your project. The architecture is far away from being the best, and the code does not follow the best practices. Here are some tips to improve it:

  • Create DTOs for your endpoints. There are always data you don’t want to expose or extra data you’d like to get from the request.
  • Use the repository pattern for more consistency in your code.
  • Use services (that depend on repositories) to add business logic.
  • Use ApiEnpoints or MediatR to remove controllers (which is an anti-pattern).
  • Use Aggregate and Specification for a better DDD approach.
  • Use a real database system (SQL Server, PostgreSQL, etc.)