I wanted to show a simple example of how Entity Framework Core works in .NET (specifically .NET 7 in this example). Entity Framework is what's known as an "Object-relational mapper" (or ORM for short) which allows developers to create classes that can be mapped to a database.
I've built a directory containing information about soccer players, teams, leagues and stadiums. The project (which can be found here) is a .NET core project and uses Razor Pages for the front-end views.
Below you can see a list of all of the players that have been added to the directory, and if I select "details" for one of the players, I can view further information (namely, which team they play for):
You can see how the players' Team Name
matches against the name of the team in the application:
In my database, I want separate tables for Teams
, Players
, Leagues
and Stadiums
so the first step is to create classes for each of those:
public class Team
{
public int TeamID { get; set; }
public string Name { get; set; }
public int LeagueID { get; set; }
public League League { get; set; }
public int StadiumID { get; set; }
public ICollection<Player> Players { get; set;}
}
public class Player
{
public int PlayerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int TeamID { get; set; }
public PlayerPosition Position { get; set; }
}
public class League
{
public int LeagueID { get; set; }
public ICollection<Team> Teams { get; set; }
public int NumberOfTeams { get; set; }
public string Name { get; set; }
}
public class Stadium
{
public int StadiumID { get; set; }
public int TeamID { get; set; }
public int Capacity { get; set; }
public string Name { get; set; }
}
The relationships of those tables is as followed:
One
Team
can have manyPlayers
One
Team
has oneStadium
One
League
has manyStadiums
Each class has an ID property (which will represent the primary key in the database). Some classes also have an ID of another type (e.g. the Stadium
class has an ID for TeamID
). This represents a foreign key in the database. Also, you'll notice how the Team
class contains an ICollection
of type Player
, and the League
class contains an ICollection
of type Team
. When we create a class with an ICollection, the table will be represented as a one-to-many relationship in the database.
I've also created a separate class containing seed data, so we will have initial data in the database after running the application for the first time:
public static class DbInitializer
{
public static void Initialize(TeamContext context)
{
if (context.Teams.Any())
{
return;
}
var leagues = new League[]
{
new League{Name="Ligue 1",NumberOfTeams=20},
new League{Name="English Premier League",NumberOfTeams=20},
new League{Name="La Liga",NumberOfTeams=20},
new League{Name="Serie A",NumberOfTeams=20},
new League{Name="Bundesliga",NumberOfTeams=18}
};
context.Leagues.AddRange(leagues);
context.SaveChanges();
var teams = new Team[]
{
new Team{Name="Paris Saint Germain",LeagueID=1,StadiumID=1},
new Team{Name="Tottenham Hotspur",LeagueID=2,StadiumID=2},
new Team{Name="Real Madrid",LeagueID=3,StadiumID=3},
new Team{Name="AC Milan",LeagueID=4,StadiumID=4},
new Team{Name="Bayern Munich",LeagueID=5,StadiumID=5},
new Team{Name="Chelsea",LeagueID=2,StadiumID=6},
new Team{Name="Internazionale",LeagueID=4,StadiumID=4}
};
context.Teams.AddRange(teams);
context.SaveChanges();
var players = new Player[]
{
new Player{FirstName="Lionel",LastName="Messi",TeamID=1,Position=PlayerPosition.Midfielder},
new Player{FirstName="Harry",LastName="Kane",TeamID=2,Position=PlayerPosition.Forward},
new Player{FirstName="Vinicius",LastName="Jr",TeamID=3,Position=PlayerPosition.Forward},
new Player{FirstName="Rafael",LastName="Leao",TeamID=4,Position=PlayerPosition.Forward},
new Player{FirstName="Joshua",LastName="Kimmich",TeamID=5,Position=PlayerPosition.Defender},
new Player{FirstName="Enzo",LastName="Fernandez",TeamID=6,Position=PlayerPosition.Midfielder},
new Player{FirstName="Lautaro",LastName="Martinez",TeamID=7,Position=PlayerPosition.Forward}
};
context.Players.AddRange(players);
context.SaveChanges();
var stadiums = new Stadium[]
{
new Stadium{Name="Parc de Princes",TeamID=1,Capacity=50000},
new Stadium{Name="Tottenham Hotspur Stadium",TeamID=2,Capacity=60000},
new Stadium{Name="Santiago Bernabeu Stadium",TeamID=3,Capacity=80000},
new Stadium{Name="San Siro",TeamID=4,Capacity=75000},
new Stadium{Name="Aliianz Arena",TeamID=5,Capacity=75000},
new Stadium{Name="Stamford Bridge",TeamID=6,Capacity=40000}
};
context.Stadiums.AddRange(stadiums);
context.SaveChanges();
}
}
Once the Initialize()
method is run, you can see it first checks context.Teams
is populated, and if it's not (i.e. if this is the first time we're running the application) it creates several objects and saves them to the database. The TeamContext
class is a class that inherits from Entity Framework's DbContext
class and is used to define how we want our database to be setup. You can set which tables (DbSet
) should be created from which classes and anytime we want to update our database we can access this object (e.g. in our DbInitializer
above we're adding to a list then calling context.SaveChanges()
to save those changes to the database.)
public class TeamContext : DbContext
{
public TeamContext (DbContextOptions<TeamContext> options)
: base(options)
{
}
public DbSet<Team> Teams { get; set; }
public DbSet<Player> Players { get; set; }
public DbSet<League> Leagues { get; set; }
public DbSet<Stadium> Stadiums { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Team>().ToTable("Team");
modelBuilder.Entity<Player>().ToTable("Player");
modelBuilder.Entity<League>().ToTable("League");
modelBuilder.Entity<Stadium>().ToTable("Stadium");
}
}
As mentioned before, we're using Razor Pages for the views. .NET makes creating the views for Create, Read, Update and Delete of entities really easy. I initially created razor views for the player entities in Visual Studio like so:
- Right-click on the folder where you want your views to be
- Hover over "Add" and select "Razor Page"
- Select "Razor Pages using Entity Framework (CRUD)" and hit "Add"
- In the dialogue that appears, set "Model Class" to whichever class you want to create razor pages for (
Player
in my case) - Select your context class as the "DbContext class" and hit "Add"
Visual Studio will then create some razor pages for you:
When you run your application, you'll be able to see all of the rows from your database:
Application:
I only wanted to give a simple preview of what can be done with Entity Framework, but if this is something that interests you and you want to go further in-depth with all the possibilities, I recommend checking out the official docs where you can also find a great tutorial which will guide you through building your very own .NET Core web application.
Top comments (1)
I'm not saying this is a bad article. I do, however, want to highlight to newcomers that Entity Framework comes with its own demons. It is handy for sure, and what I think people love the most is that they don't have to write SQL.
All this goodness is a double edge sword, as time will demonstrate. Be ready to combine with another ORM. I personally recommend Dapper.