-
Notifications
You must be signed in to change notification settings - Fork 0
EF Configuration
Entity framework is technology that allows you manage your database without writing SQL queries using OOP approach.
By first, let's take a look to some EF components with what we will be dealing.
-
Context is class, that represents session to our database. Context performs entity tracking,receives data from db and does changes to db.
-
DbSet is collection that represents certain table of entity.
-
Model is class, that represents entity.
-
FluentAPI is tool, that used for configuring models representation in database.Table in database will be generated based on FleuntAPI configuration.
Tracking is useful feature of EF, that allows you track changes of concrete entity and apply these changes when context.SaveChanges()
is called.
Tracking in EF is enabled by default, but if you don't need track your entities (especially when you need entire collection of entities) you can call .AsNoTracking()
this method will execute query directly to database, to avoid tracking by DBContext.
Also it's important to know that you can track only one unique instance of entity. Entity's unique is provided by primary key of entity (Id). Like there can be only one instance with unique Id. It's called Identity resolution. Check more
Entity can have states. Query to database will be generated based on these states.
- Added: entity is being tracked by the context but does not yet exist in the database(INSERT command)
- Modified: entity is already exists in database and it was modified.(UPDATE command)
- Deleted: entity exists in database, but when we call
.OnSaveChanges()
it will be deleted from database(DELETE command) - Unchanged: entity is tracked, but all changes won't be applied.
- Detached: entity is not tracked by context.
So let's start configuring EF in our API. It's better to write all EF related code in separate project.
- Firstly, create a class library project in your solution
- Delete the default class and add following folders
- Install necessary packages
And this one in your web API project
- Add context class in folder Context
public class SandboxContext : DbContext
{
public DbSet<Item> Items { get; set; }
public SandboxContext(DbContextOptions<SandboxContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
}
}
Here, we have overridden OnModelCreating
method for adding FluentAPI configurations, that we will discuss in next step. Method modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly)
will load all configurations automatically.
- Let's configure FluentAPI. In this tutorial we are using some simple class like that:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public int Amount { get; set; }
}
Create ItemConfiguration in Configurations folder and write following code:
public void Configure(EntityTypeBuilder<Item> builder)
{
builder
.HasKey(item => item.Id);
builder
.Property(item => item.Name)
.HasMaxLength(60);
builder.ToTable("Items");
}
In configuration class we can configure structure of our entity representation table. We can set keys, additional information about properties and relationships between tables.This configuration will be applied to migration when we create migration.
- We have everything ready to create our database and first migration.But we still need connect our DB context to our web API.Lets do that.
In your appsettings.json file add this section:
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=Sandbox;Trusted_Connection=True;"
}
In this tutorial we are using MSSQL server, so make sure that you have it installed.
Now add your DBContext to services in Program.cs file:
builder.Services.AddDbContext<SandboxContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
7.Now lets add migration. ❗Don't forget build your entire solution before adding migration❗
Write Add-Migration init
in PM console, also make sure that you`re running this command in DataAccess project.
If everything went smooth you will see new folder Migrations in DataAccess project.
Let's add migrations to database.For that run Update-Database
command in PM console. You must run these two commands every time when you need apply changes to database structure.
If this command has executed successfully you will see your database and table in SQL Server Object explorer
🎉YAY! You have created database🎊
There are 3 types of relationships how our entities can be related to each other.
- One-to-One: one entity can have only another one
- One-to-Many: one entity can consist many others
- Many-to-Many: many entities can consist many others entities
This is the most used type of relationship. Let's imagine that we have entities Delivery and Item and Delivery can consist many items.
Add navigation properties in these classes by first.
Delivery:
public class Delivery
{
public int Id { get; set; }
public string StartPoint { get; set; }
public string EndPoint { get; set; }
public IEnumerable<Item> Items { get; set; }
}
Item:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public int Amount { get; set; }
public int DeliveryId { get; set; }
public Delivery Delivery { get; set; }
}
Navigation property will be loaded by entity framework when we execute query.
Now, lets add relationship to ItemConfiguration class
builder
.HasOne(item => item.Delivery)
.WithMany(delivery => delivery.Items)
.HasForeignKey(item => item.DeliveryId)
.OnDelete(DeleteBehavior.Restrict);
DeleteBehavior.Restrict
means that when we delete object, all related data to this entity will be set null in dependent entities. Like we have some property and that property will be null.
So don't forget to run Add-Migration addOneToMany
and Update-Database
commands. Only after creating and updating database relationship will be created. There is relationship that we've configured
This relationship means only one entity corresponds to another one.Relationship between passport and human can be good example of it. Let's consider that we have following entities:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Passport Passport { get; set; }
}
public class Passport
{
public int UserId { get; set; }
public string SerialNumber { get; set; }
public DateTime DateOfIssue { get; set; }
public User User { get; set; }
}
So, let's set up relationship between them, add to the passport's configuration class this configuration:
builder.HasOne(passport => passport.User)
.WithOne(user => user.Passport)
.HasForeignKey<Passport>(passport => passport.UserId);
You can notice that passport entity doesn't have own primary key, we are using foreign key of user as primary key, in configuration it looks like that:
builder.HasKey(passport => passport.UserId);
Everything is ready for update of database. Make sure that you've added new DbSets to DbContext class and run commands for creating and updating database.
This is relationship that we've made
This type of relationship describes situation when many entities related to another entities. For example many users can have many achievements. This one can be good example for describing many-to-many relationship.
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<UserAchievement> UserAchievements { get; set; }
}
public class Achievement
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<UserAchievement> UserAchievements { get; set; }
}
We have to create joining entity. That entity will keep keys from user and achievement.
public class UserAchievement
{
public int UserId { get; set; }
public User? User { get; set; }
public int AchievementId { get; set; }
public Achievement? Achievement { get; set; }
}
Also let's add configuration for joining entity
public class UserAchievementConfiguration : IEntityTypeConfiguration<UserAchievement>
{
public void Configure(EntityTypeBuilder<UserAchievement> builder)
{
builder.HasKey(ua => new
{
ua.AchievementId,
ua.UserId
});
builder.HasOne<User>(ua => ua.User)
.WithMany(user => user.UserAchievements)
.HasForeignKey(ua => ua.UserId);
builder.HasOne<Achievement>(ua => ua.Achievement)
.WithMany(achievement => achievement.UserAchievements)
.HasForeignKey(ua => ua.AchievementId);
builder.ToTable("UserAchievements");
}
}
Everything is ready! Now add joining entity DbSet to DbContext, rebuild your solution and run command for creating migration and updating database. Many-to-Many relationship in database will look similar to this: