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
- Visual Studio
- .NET SDK (should be installed with Visual Studio)
Create the solution
Create a new solution and name it EfTutorial
. Then, create three projects within the solution:
- WebAPI
- Core
- Infrastructure
Your project architecture you look like this:
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.
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>
.
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.
After creating the migration, you can apply it by using the Update-Database
command.
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.
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
}
]
}
If you list all the customers, you should see the one you just added.
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.)