Announcing Entity Framework Core 6.0 Preview 5: Compiled Models

Today, the Entity Framework Core team announces the fifth preview release of EF Core 6.0. This release includes the first iteration of compiled models. If startup time for your application is important and your EF Core model contains hundreds or thousands of entities, properties, and relationships, this is one release you don’t want to ignore.

TL;DR;

Compiled models dramatically reduce startup time for your application.
The models are generated (similar to how migrations are) so they should be refreshed whenever your model changes.
Some features are not currently supported by compiled models, so be aware of the limitations when you try them out.

Background

How does 10x performance sound to you? Our team created a sample project with a DbContext that contains 449 entity types, 6,390 properties and 720 relationships. I wrote a console app that loops several times, creates a new instance of a DbContext and loads a set of entities with no filters or ordering. The start-up time for the first run consistently takes around two seconds on my laptop, with subsequent cached instances weighing in at about 1.5 seconds. Here’s the output from a run:

$ dotnet run -c Release
Model has:
449 entity types
6390 properties
720 relationships
Instantiating context…
It took 00:00:02.1603163.
Instantiating context…
It took 00:00:01.6268628.
Instantiating context…
It took 00:00:01.7144346.
Instantiating context…
It took 00:00:01.6090380.
Instantiating context…
It took 00:00:01.7049987.

After testing the baseline application, I used the new EF Core tools Command Line Interface (CLI) feature to optimize the DbContext:

dotnet ef dbcontext optimize -output-dir MyCompiledModels –namespace MyCompiledModels

The tool gave me instructions to add a single line of code to my DbContext configuration:

options.UseModel(MyCompiledModels.BlogsContextModel.Instance);

I made the update and re-ran the code to receive a 10x performance gain with the initial model taking 257ms to complete. The cached model reduced additional calls to just 10ms.

$ dotnet run -c Release
Model has:
449 entity types
6390 properties
720 relationships
Instantiating context…
It took 00:00:00.2573627.
Instantiating context…
It took 00:00:00.0132345.
Instantiating context…
It took 00:00:00.0119556.
Instantiating context…
It took 00:00:00.0101717.
Instantiating context…
It took 00:00:00.0139057.

A peek at the query pipeline

EF Core performs quite a bit of work to get from your application to returning the first result of the first query your application processes. Let’s break down the following two statements and go “behind the scenes” to see what happens.

using var myContext = new MyContext();
var results = myContext.MyWidgets.ToList();

DbContext instantiation

The first step is creating an instance of the context. The first time a DbContext is created, EF Core will create and compile delegates to set the table properties you expose by using DbSet<Entity>. This simply creates the delegates to set the properties so you can query them right away.

Performance tip: you can avoid the overhead of DbSet initialization by using an alternate approach such as the context.Set<Entity>() API call.

DbContext (lazy) initialization

After the DbContext is created, EF Core “goes to sleep” until you use it. The first time you use a context by accessing one of its APIs (such as navigating an entity and returning results), the context is initialized. This will run the OnConfiguring method to establish the proper provider and database connections as well as other settings. For example, this is the perfect place to use the simple logging feature by calling the new LogTo extension on the options builder.

Service provider

EF Core uses a service-based architecture and has an internal dependency injection framework. This provider is built internally but is designed to work with external DI solutions such as the service provider in ASP.NET Core.

Performance tip: much of the overhead described so far can be mitigated by using context pooling. This enables a pool of reusable context instances that are already initialized.

Model building

To understand how a domain object (C# class) relates to the tables and relationships in the database, EF Core builds an internal model that represents all the types, properties, constraints, and relationships that it finds in your DbContext. This is a metadata model and includes the call to OnModelCreating that can be overridden to provide fluent configuration of the model.

Query compilation

A major reason why developers use EF Core is its ability to parse Language Integrated Queries (LINQ) into the database dialect. This is an advanced stage because it involves traversing a potentially complex expression tree and translating it into SQL. Something trivial like a projection:

var projection = myQuery.Select(obj => new { id = obj.EntityId, name = obj.Identifier });

Seems easy enough to translate:

SELECT EntityId, Identifier FROM …

But what about something more complicated, like this?

var pairs = (from a1 in context.Attendees
from a2 in context.Attendees
where a1.Id != a2.Id
select new
{
a1 = a1.Id,
a1LastName = a1.LastName,
a1FirstName = a2.FirstName,
a2 = a2.Id,
a2LastName = a2.LastName,
a2FirstName = a2.FirstName,
sessionCount =
a1.Sessions.Select(s => s.Id)
.Intersect(a2.Sessions.Select(s => s.Id)).Count()
}).OrderByDescending(shared => shared.sessionCount)
.Take(5);

This is ultimately parsed into native SQL, intersection and all. The first time that EF Core encounters a query, it parses the query to determine which parts are dynamic. It then compiles the static parts of the query and parameterizes the dynamic aspects to expedite translation into SQL by using a SQL template.

Run the query

Finally! The query is now run. To avoid the overhead of performing these steps every time, EF Core caches the delegates for DbSet properties, the internal service provider, the constructed model, and the compiled query. This results in much faster performance after the queries are successfully run the first time.

You can visualize these steps using the following diagram (note the cache boxes have strike-through to show they are disabled for our benchmark tests):

Although most of the pipeline is already streamlined, model compilation was an area we knew could improve.

A note on source generators. The approach the team chose is to provide a command that generates the source code files that you can then incorporate into your project to build the compiled model. We are often asked why we didn’t choose source generators. The answer is that source generators run as user code inside the Visual Studio process. EF Core must build and run the context to obtain information about the model. If an exception is thrown as part of the process, this could potentially force Visual Studio to hang or crash.

As with most technology, compiled models do have trade-offs. Let’s look at the pros and cons.

Pros and cons

The pros should be clear. As your model grows larger, your startup time remains fast. Here is a comparison of startup time between compiled and non-compiled models based on the size of the model.

Here are some cons to consider:

Global query filters are not supported.

Lazy loading proxies are not supported.

Change tracking proxies are not supported.
Custom IModelCacheKeyFactory implementations are not supported.
The model must be manually synchronized by regenerating it any time the model definition or configuration change.

Tip: if supporting any of these features is critical to your success, please find the issue and upvote it or add your comments and thoughts, or file a new issue to let us know.

Now you’ve learned the background. How do you get started?

In conclusion

To start using compiled models today, reap the performance benefits and have the opportunity to provide us with feedback before we release the final EF Core 6.0 version, start by grabbing the latest preview (instructions are below) and installing the latest EF Core CLI. The new tool command looks like this (all parameters are optional):

dotnet ef dbcontext optimize -c MyContext -o MyFolder -n My.Namespace

Inside the NuGet package manager console you can use this:

Optimize-DbContext -Context MyContext -OutputDir MyFolder -Namespace My.Namespace

The tool will instruct you to add a line like this to your options configuration:

opts.UseModel(My.Namespace.MyContextModel.Instance);

We hope you benefit from this new feature and can provide us with early feedback. Check out the EF Core 6.0 plan. In addition to other work, the team has prioritized a number of Azure Cosmos DB provider features. Please upvote the features that are important to you and share any feedback you may have! Other features in the preview 5 release will be posted in EF Core 6.0 What’s New.

How to get EF Core 6.0 previews

EF Core is distributed exclusively as a set of NuGet packages. For example, to add the SQL Server provider to your project, you can use the following command using the dotnet tool:

dotnet add package Microsoft.EntityFrameworkCore.SqlServer –version 6.0.0-preview.5.21301.9

This following table links to the preview 5 versions of the EF Core packages and describes what they are used for.

Package
Purpose

Microsoft.EntityFrameworkCore
The main EF Core package that is independent of specific database providers

Microsoft.EntityFrameworkCore.SqlServer
Database provider for Microsoft SQL Server and SQL Azure

Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite
SQL Server support for spatial types

Microsoft.EntityFrameworkCore.Sqlite
Database provider for SQLite that includes the native binary for the database engine

Microsoft.EntityFrameworkCore.Sqlite.Core
Database provider for SQLite without a packaged native binary

Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite
SQLite support for spatial types

Microsoft.EntityFrameworkCore.Cosmos
Database provider for Azure Cosmos DB

Microsoft.EntityFrameworkCore.InMemory
The in-memory database provider

Microsoft.EntityFrameworkCore.Tools
EF Core PowerShell commands for the Visual Studio Package Manager Console; use this to integrate tools like scaffolding and migrations with Visual Studio

Microsoft.EntityFrameworkCore.Design
Shared design-time components for EF Core tools

Microsoft.EntityFrameworkCore.Proxies
Lazy-loading and change-tracking proxies

Microsoft.EntityFrameworkCore.Abstractions
Decoupled EF Core abstractions; use this for features like extended data annotations defined by EF Core

Microsoft.EntityFrameworkCore.Relational
Shared EF Core components for relational database providers

Microsoft.EntityFrameworkCore.Analyzers
C# analyzers for EF Core

We also published the 6.0 preview 5 release of the Microsoft.Data.Sqlite.Core provider for ADO.NET.

Thank you from the team

A big thank you from the EF team to everyone who has used EF over the years!


Arthur Vickers


Andriy Svyryd


Brice Lambson


Jeremy Likness


Maurycy Markowski


Shay Rojansky


Smit Patel

Thank you to our contributors!

We are grateful to our amazing community of contributors. Our success is founded upon the shoulders of your efforts and feedback. If you are interested in contributing but not sure how or would like help, please reach out to us! We want to help you succeed. We would like to publicly acknowledge and thank these contributors for investing in the success of EF Core 6.0.

AkinSabriCam
alexernest
alexpotter10
Ali-YousefiTelori

#1
#1
#1

#1, #2

alireza-rezaee
andrejs86
AndrewKitu
ardalis

#1
#1
#1
#1

CaringDev
carlreid
carlreinke
cgrevil

#1, #2

#1, #2

#1, #2

#1

cgrimes01
cincuranet
dan-giddins
dannyjacosta

#1

#1, #2, #3, #4

#1

#1, #2

dennisseders
DickBaker
ErikEJ
fagnercarvalho

#1, #2, #3, #4, #5, #6

#1

#1, #2, #3, #4, #5, #6, #7, #8, #9, #10, #11, #12

#1, #2

FarshanAhamed
filipnavara
garyng
Geoff1900

#1

#1, #2

#1, #2, #3

#1

gfoidl
Giorgi
GitHubPang
gurustron

#1, #2

#1, #2, #3, #4

#1
#1

hez2010
HSchwichtenberg
jaliyaudagedara
jantlee

#1, #2

#1

#1, #2

#1

jeremycook
jing8956
joakimriedel
joaopgrassi

#1
#1

#1, #2

#1, #2

josemiltonsampaio
KaloyanIT
khalidabuhakmeh
khellang

#1

#1, #2, #3, #4

#1, #2

#1

koenbeuk
kotpal
larsholm
lauxjpn

#1
#1

#1, #2

#1, #2

leonardoporro
lexkazakov
mariuz
marodev

#1
#1
#1

#1, #2

MartinWestminster
Marusyk
MattKomorcec
MaxG117

#1

#1, #2, #3, #4, #5, #6, #7, #8, #9, #10, #11, #12, #13, #14, #15, #16

#1, #2

#1

mefateah
meggima
mguinness
michalczerwinski

#1

#1, #2

#1

#1, #2, #3, #4, #5, #6

mrlife
msawczyn
MSDN-WhiteKnight
natashanikolic

#1, #2, #3, #4

#1
#1
#1

nmichels
nschonni
OKTAYKIR
OOberoi

#1, #2

#1, #2, #3, #4

#1
#1

Oxyrus
pkellner
ptupitsyn
ralmsdeveloper

#1
#1
#1

#1, #2

RaymondHuy
riscie
SergerGood
Shirasho

#1, #2, #3, #4, #5, #6, #7, #8

#1, #2

#1, #2, #3, #4, #5, #6, #7, #8, #9, #10

#1

SimonCropp
stevendarby
Strepto
teo-tsirpanis

#1, #2

#1, #2

#1, #2

#1

the-wazz
tkp1n
Tomkaa
umitkavala

#1, #2

#1, #2

#1, #2

#1, #2, #3, #4

uncheckederror
Varorbc
vincent1405
vonzshik

#1
#1

#1, #2

#1, #2, #3, #4

vytotas
wdesgardin
wmeints
yesmey

#1

#1, #2

#1, #2

#1, #2, #3, #4, #5, #6

The post Announcing Entity Framework Core 6.0 Preview 5: Compiled Models appeared first on .NET Blog.

Flatlogic Admin Templates banner

Leave a Reply

Your email address will not be published. Required fields are marked *