Intro to Entity Framework Core 5 - New features

moe23

Mohamad Lawand

Posted on March 2, 2021

Intro to Entity Framework Core 5 - New features

In this article I will be explaining to you what is Entity Framework, what benefits does it provide, will do a quick overview and then we will discuss the new features in .Net 5

So the Agenda for today:

  • What do we need?
  • What is Ef Core
  • Some of the benefits of Ef Core
  • Overview of Ef Core
  • New Features in Ef 5
  • Demo New Features

You can watch the full video on Youtube

If you find this video helpful, please like, share and subscribe as it will really help the channel.

Before we start you will need to have

Source code:
https://github.com/mohamadlawand087/v18-EfCore5

What is Entity Framework?

Entity framework core is a data access API, its an object relational mapper (ORM). It can be used cross platform across different technologies from AspNetCore, WPF, Xamarin, Blazor, Winform.

It has support to many different database technologies:

  • SQL Server
  • SQLite
  • PostgreSQL
  • MySQL
  • Azure Cosmos

Benefits of Ef

  • One of the main benefits of Ef Core is using LINQ (Language Integrated Language)
  • It has UOW (Unit of Work) implementation out of the box
    • it will track changes
    • handle concurrency
  • Data binding friendly

Getting Started

dotnet tool install --global dotnet-ef

dotnet new mvc -n "EfCoreDemo" -au none

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Enter fullscreen mode Exit fullscreen mode

Lets create a customer class inside our Models folder

public class Customer
{
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    public string Email { get; set; }

    public int PhoneNumber { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Ef Core will be able to figure out automatically that our primary key will be the Id and since it's an Int it will make it auto increment by default.

Then it will look at the data annotation that we have added to every field and get more information on how the database should be setup, for example we have a Required annotation on Name and Email, Ef core will automatically assign not null option in the database based on these annotation.

Alt Text

Now lets create our ApplicationDbContext which is the main part of having Ef core integrated in our application.

Let us create a new folder in the root of our application and add a class to it called ApplicationDbContext.

We inherit from the DbContext class which is the main component in Ef Core

// DbContext is where the magic happens 
public class ApplicationDbContext : DbContext
{
    // a Db set is where we tell entity framework where to map a class (entity) to a table
    public DbSet<Customer> Customers { get; set; }

    // This is the run time configuration of 
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    { }

    // ModelBuilder is the fluent mapping 
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Customer>()
            .Property(a => a.RowVersion)
            .IsRowVersion(); // Cuncurrency property using fluent mapping

        base.OnModelCreating(modelBuilder);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now lets setup our Startup class to inject Ef core middleware

// We are adding the DI services - We setup a Db Context
services.AddDbContext<ApplicationDbContext>(options => 
    // Adding configuration to use Sqlite with our application
    options.UseSqlite( // adding the connection string path to our db
            Configuration.GetConnectionString("DefaultConnection")));
Enter fullscreen mode Exit fullscreen mode

As well we need to add the connection string to the AppSettings.json

"ConnectionStrings": {
    "DefaultConnection": "DataSource=app.db;Cache=Shared"
  }
Enter fullscreen mode Exit fullscreen mode

Now lets run the migrations and update the database

dotnet ef migrations add "Initial Migration"
dotnet ef database update
Enter fullscreen mode Exit fullscreen mode

Let us now write the code to get all customer data from the database, inside our HomeController we can add the following to the Index action

private readonly ILogger<HomeController> _logger;
private readonly ApplicationDbContext _context;

public HomeController(ILogger<HomeController> logger, ApplicationDbContext context)
{
    _logger = logger;
    _context = context;
}

public async Task<IActionResult> Index()
{
    var customers = await _context.Customers
                            .Where(x => x.Name.Contains("a"))
                            .OrderBy(o => o.Name)
                            .ToListAsync();
    return View(customers);
}
Enter fullscreen mode Exit fullscreen mode

New Features

Now that we have covered a quick overview of Ef core and its features, let us now discuss some of the new changes that came to Ef Core 5

Debug:

Debug view when running the debugger, we can see when hovering over the query a debugView option

Alt Text

So we can see the Query field is a SQL representation of our LINQ query which we can copy directly from there and run it in a SQL management studio to test the output of the query and debug any issue we might face

Another we can get our Query is by using the extension method on the query

.ToQueryString()

// the ToQueryString will generate the SQL code so we can test and debug issues
// without the need to be in debug mode
var sqlStr = customers.ToQueryString();
Console.WriteLine(sqlStr);
Enter fullscreen mode Exit fullscreen mode

The last option is enabling general logs across our app, inside our startup class where we added the ApplicationDbContext in the DbContextOptions we can enable logging from there

services.AddDbContext<ApplicationDbContext>(options => 
                // Adding configuration to use Sqlite with our application
                options.UseSqlite( // adding the connection string path to our db
                        Configuration.GetConnectionString("DefaultConnection"))
                                        // Enable the log level and where do we want to output the logs
                    .LogTo(Console.WriteLine, LogLevel.Information));
Enter fullscreen mode Exit fullscreen mode

Mapping:

Many-to-Many enhancements, what is many to many relationships. Lets say we have a table of customers and a table for groups Many-To-Many relationship means one user can belong to different groups and the same group can have multiple users

Alt Text

The way its done in a database we create a table in the middle between the 2 tables that have the Many-To-Many relations and we call that table the Join Table in our case the Join Table is the CustomerGroup table. And every row inside that table will link 1 customer to 1 group.

Lets see this in code how it was implemented previous to .Net 5 and then we can see how we can update it to take advantage of .Net 5 features , we already have a customer table lets add the 2 other tables and do create the Many-To-Many relationships

Inside the Models folder lets add 2 classes Group and CustomerGroup

public class Group
{
    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<CustomerGroup> Groups {get;set;}
}
Enter fullscreen mode Exit fullscreen mode

Customer group (Join Table)

public class CustomerGroup
{
    public int Id { get; set; }
    public Customer Customer { get; set; }
    public Group Group { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

And we need to update Customer class to the following

public ICollection<CustomerGroup> Groups {get;set;}
Enter fullscreen mode Exit fullscreen mode

The next step is to update the ApplicationDbContext

public DbSet<Group> Groups { get; set; }
public DbSet<CustomerGroup> CustomerGroups { get; set; }
Enter fullscreen mode Exit fullscreen mode

Lets create our migration scripts so our database will be updated

dotnet ef migrations add "Added M2M relationships"
dotnet ef database update
Enter fullscreen mode Exit fullscreen mode

Now let see how we can update this code into .Net 5, the first thing we are going to do is delete the CustomerGroup Table and then we need to do some updates on the Customer and Group Model.

For the customer Model

public ICollection<Group> Groups {get;set;}
Enter fullscreen mode Exit fullscreen mode

For the Group Model

public ICollection<Customer> Customers {get;set;}
Enter fullscreen mode Exit fullscreen mode

We can see here that we have removed the JoinTable and we have delegated the work to EF Core to create and manage the JoinTable for us.

Another update we need to do is to our LINQ query since we don't have have the CustomerGroup table anymore the query will look much simpler and easier to read

var customersElec = _context.Customers.Where(x => x.Groups.Any(g => g.Name == "Electronics"));
Enter fullscreen mode Exit fullscreen mode

So how did this magic happen, EF Core in the background analysed the models that we have and created the JoinTable for us and took control of managing it.

Inheritance Mapping

Its a hierarchy of .Net type base class, sub class and we want to map those to a relational database

Lets us add additional models to see how we used to do before .Net 5 and then we can see how its implemented with the new features in .Net 5

Inside our Models folder lets add 2 new models

public class VipCustomer : Customer
{
    public string VipTeir { get; set; }
}
Enter fullscreen mode Exit fullscreen mode
public class CorporateCustomer : Customer
{
    public string CompanyName { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

We need to update our ApplicationDbSettings to know about these new models, inside our OnModelCreating method we add the following

// This will inform Ef Core that we want to map these tables 
modelBuilder.Entity<VipCustomer>();
modelBuilder.Entity<CorporateCustomer>();
Enter fullscreen mode Exit fullscreen mode

Let us add our migration scripts

dotnet ef migrations add "Adding 2 tables"
dotnet ef database update
Enter fullscreen mode Exit fullscreen mode

If we look at our database we see that instead of having 3 tables that represent the hierarchy that we wanted we have a single table Customer with all the fields inside of it. This method of implementation is called TPH - table per hierarchy.

The way the Ef core differentiate between which row for which table is via the Discriminator field. This is feature has been available in Ef core since previous versions now in EF Core 5 lets see how we can do the separation.

Let us update our ApplicationDbContext

modelBuilder.Entity<VipCustomer>().ToTable("VipCustomers");
modelBuilder.Entity<CorporateCustomer>().ToTable("CorporateCustomers");
Enter fullscreen mode Exit fullscreen mode

Let us add our migrations now

dotnet ef migrations add "Adding 2 tables"
dotnet ef database update
Enter fullscreen mode Exit fullscreen mode

Now if we look at our database the result are really different instead of having 1 big table which represent the 3 tables we can see we have 3 tables, each table representing the model that we have this is called TPT - table per type. So now the customer is spread across different table since we have implemented TPT format.

This implementation is simple to understand as every model has a table which represent it, however the performance on the database could take a hit as every time we need to query a customer joins between tables needs to happen, and joins are heavy operations on the database. For example if we have 6 table hierarchy there would be 6 joins which is a very heavy performance query.

Although this feature has been released the recommended way is still to use TPH by default.

Thank you for reading.

💖 💪 🙅 🚩
moe23
Mohamad Lawand

Posted on March 2, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related