NET Core 3 update to “Entity Framework Core in Action” book

Last Updated: November 3, 2019 | Created: October 19, 2019

With the release of EF Core 3 I wanted to provide updates to my book, “Entity Framework Core in Action”. This article covers the whole of the book and provides the updated information. But I also plan to write two more articles that go with the big, under the hood, changes in EF Core 3, they are: a) query overhaul and b) the addition of the NoSQL database, Cosmos DB, as a database provider. The list of articles in this series is:

  • Part 1: NET Core 3 update to “Entity Framework Core in Action” book (this article)
  • Part 2: Entity Framework Core performance tuning – reworked example with NET Core 3 (not written yet)
  • Part 3: Building a robust CQRS database with EF Core and Cosmos DB – NET Core 3 (not written yet)

NOTE: This article is written to supplement/update the book “Entity Framework Core in Action”. It is not a general introduction to EF Core 3 – please look at the Microsoft documentation for the changes in EF Core 3.

TL;DR; – summary

  • The release of EF Core 3 made small changes to the code you write, but there are big changes inside, i.e. query overhaul and support for NoSQL databases (Cosmos DB).
  • For the most part the “Entity Framework Core in Action” book (which covered up to EF Core 2.1) doesn’t change a lot.
  • There are 19 small changes in EF Core 3 that effect the book. In my opinion only two of them are really important:
  • The big changes in EF Core 3 which warrant their own articles are:
    • Query overhaul, where the way that LINQ is converted to database commands has been revamped. I mentions this in this article but going to more detail another article.
    • Support for NoSQL databases (Cosmos DB) which I will cover in separate articles.

List of changes by chapter

I’m now going to go through each chapter in my book and tell you what has changed. But first here is a summary:

  • Chapter 1 – 3 changes (medium, setup)
  • Chapter 2 – 1 change (important)
  • Chapter 3 – no changes
  • Chapter 4 – no changes
  • Chapter 5 – 2 changes (small, code)
  • Chapter 6 – 1 change (small, config)
  • Chapter 7 – 3 changes (small, config)
  • Chapter 8 – 1 change (optional, config)
  • Chapter 9 – 2 changes (small, rename)
  • Chapter 10 – no changes
  • Chapter 11 – 1 change (optional)
  • Chapter 12 – 2 changes (medium)
  • Chapter 13 – see a new article looking at the performance changes.
  • Chapter 14 – 1 change (medium)
  • Chapter 15 – 2 changes (small)

NOTE: There are other changes in EF Core 3.0 that I don’t list because the topic was not covered in my book. Typically these other changes are about features that are too deep to cover in a 500-page book. For a full list of all the breaking changes in EF Core 3 see this link to the Microsoft documentation.

Chapter 1 – Introduction to Entity Framework Core

This chapter introduced EF Core, described what it does and gives a simple application that accesses a database to start.  Much of this stays the same, but here are the changes:

Pages 9 to 11, Installing, creating application, and downloading example

The description uses VS2017 but if you want to use EF Core 3 you should use VS2019 instead (note: installing VS2019 should also install NET Core 3 SDK). The steps are the same but VS2019 is a bit easier.

NOTE: If you want to download an EF Core 3 version for the companion repo EfCoreInAction then I have created new branch called Chapter01-NetCore3 that you can download.

Pages 17 to 19, Reading data from the database

In listing 1.2 I introduce the EF Core method called AsNoTracking. This tells EF Core that you don’t want to update this data, which reduces the memory it take and makes it slightly faster.

EF Core 3 adds another another performance improvement by returning individual instances of each entity class, rather that returning a single instance for duplicate entities, i.e. classes of the same type and primary key (if that doesn’t make sense then read the EF Core docs on this).

Now that sounds OK, but it can produce some very subtle changes to your application, mainly in your business logic. Taking the Book/Author example in the book, if I read in two books with the same author using AsNoTracking and I would get different results in EF Core 2 from EF Core 3. Here is the code and what is returned:

var entities = context.Books
    .Where(x => x.AuthorsLink.Author.Name == "Author of two books")
    .Include(x => x.AuthorsLink)
         .ThenInclude(x => x.Author)
    .ToList();
  • EF Core 2: I get two instances of the Book classes and ONE instance of the Author class (because both books have the same author)
  • EF Core 3: I get two instances of the Book classes and TWO instance of the Author class, one for each book.

This shouldn’t be a problem if you are reading data to show to the user, but it may be a problem in your business logic if you were relying on the instances being unique. I found it in my Seed from Production tests when I was saving production data to json.

Pages 22 to 26 – Should you use EF Core in your next project?

Much of what I said there still applied but EF Core is much more mature and used by millions in real products. EF Core’s stability and coverage are much higher now than when I started to write this book.

Chapter 2 – Querying the database

There is a very significant under-the-hood change to the client vs. server evaluation feature in EF Core 3. I described the client vs. server evaluation (pages 43 to 45) and at the end I warned you that client vs. server evaluation could produce really inefficient database accessed, and by default EF wouldn’t alert you to this (you could turn on an event that would throw an exception, but by default this was off).

It turns out it is pretty easy to write a LINQ query that can’t be properly translated to a database access and were run in software, which can be very slow. The EF Core 3 the team decided to fix this problem as part of the query overhaul by restricting the client vs. server evaluation to remove these bad performing queries. In EF Core 3 you now get an InvalidOperationException thrown if the query couldn’t be translated into a single, valid database access command.

See the Conclusion section at the end of this article for some more information on how to handle such an exception.

NOTE: client vs. server evaluation is still works at the top level (and is very useful) – see the EF Core restricted client evaluation docs to show you what works.

Chapter 3 – Changed the database content

No changes.

Chapter 4 – Using EF Core in business logic

No changes.

Chapter-5 – Using EF Core in ASP.NET Core

Obviously, the big change is using ASP.NET Core 3 which changes the code you write in ASP.NET Core’s Program and Startup classes. But all of the concepts, and nearly all of the code, in this chapter stays the same. The only code changes are:

NOTE: In the companion repo EfCoreInAction I have created a branch called Chapter05-NetCore3. In this I have updated all the projects to NetStandard2.1/netcoreapp3.0. I also swapped out AutoFac for the NetCore.AutoRegisterDi library – see this article about why I did that.

Chapter 6 – Configuring nonrelational properties

There is a small change to backing fields on how data is loaded (pages 172 and 173). In EF core 2 it defaulted to loading data via the property, if present. In EF Core 3 it defaults to loading data via the private field, which is a better default – see this link for more on this.

Chapter 7 – Configuring relationships

Pages 195 to 199 – Owned types

Owned types in EF Core 3 have changes how you map an Owned type to another table (page page 198). Now you have to provide the table name as a second parameter in the Fluent APIs’ OwnOne method. See this link for more information.

NOTE: In appendix B, page 470 that EF Core 2.1 provided the [Owned] attribute which can be used instead of Fluent API configuration. I find this a simpler way to configure a class as an Owned type than using the Fluent API, but it can’t change the table mapping.

Pages 199 to 203 – Table per hierarchy section

In the table per hierarchy section I had to configure the discriminator’s AfterSaveBehavior behaviour to Save (see bold content in listing 7.22). In NET Core 3 you cannot do that anymore because the MetaData property is read-only, but it seems that the issue which needed me to set the AfterSaveBehavior property has been fixed, so I don’t need that feature.

Pages 203 to 205 – Table splitting

By request the way that tables splitting works has changed. Now the dependant part (BookDetail class in my example) is now optional. This is slightly complicated change so I refer you to the EF Core 3 breaking change section that covers this.

Chapter 8 – Configuring advanced features and handling concurrency conflicts

There is one small improvement around user defined functions (pages 209 to 213). In listing 8.4 I said you always needed to define the Schema name when configuring the user defined functions. In EF Core 3 you don’t need to set the Schema name if you are using the default schema name.

Chapter 9 – Going deeper into the DbContext

Pages 256 to 259 – Using raw SQL commands in EF Core

The EF Core SQL commands have been renamed to make the commands safer from SQL injection attacks. There are two versions of each method: one using C#’s string interpolation and one that uses {0} and parameters, e.g.

context.Books
    .FromSqlRaw("SELECT * FROM Books WHERE Price < {0}”, priceLimit)
    .ToList();
context.Books
     .FromSqlInterpolated($"SELECT * FROM Books WHERE Price < {priceLimit}”)
     .ToList();

My unit tests also show that you can’t add (and don’t need) the IgnoreQueryFilters() method before a EF Core SQL command (you did need to in EF Core 2). It seems that the methods like FromSqlRaw don’t have the query filter added to the SQL. That’s a nice tidy up as it often made the SQL command invalid, but please remember to add the query filter to your SQL if you need it.

Page 261 – Accessing EF Core’s view of the database

In EF Core 3 access to the database schema information has changed. Instead of using the method Relational you can now directly access database information with methods such as GetTableName and GetColumnName. For an example of this have a look at the DatabaseMetadata extensions in the Chapter13-Part2-NetCore3 branch.

Chapter 10 – Useful software patterns for EF Core applications

No changes.

Chapter 11 – Handling database migrations

Pages 303 to 306 – Stage 1: creating a migration

In figure 11.1 I showed you how to create an IDesignTimeDbContextFactory if you have a multi-project application. But if your EF Core 3 code is in an ASP.NET Core 3 application you don’t need to do this because EF Core 3 will call CreateHostBuilder in ASP.NET Core’s Program class to get an instance of your application DbContext. That’s a small, but nice improvement.

Chapter 12 – EF Core performance tuning

All the suggestions for improving performance are just as useful in EF Core 3, but here are a few adjustments for this chapter.

Pages 338 to 339 – Accessing the logging information produced by EF Core

The code that showed to setup logging on EF Core is superseded. The correct way to get logging information from EF Core is to use the UseLoggerFactory when defining the options. Here is an example of how this could work (see the full code in my EFCore.TestSupport library).

var logs = new List<LogOutput>();
var options = new DbContextOptionsBuilder<BookContext>()
    .UseLoggerFactory(new LoggerFactory(new[]
    {
        new MyLoggerProviderActionOut(log => logs.Add(log))
    }))
    .UseSqlite(connection)
    .Options;
using (var context = new BookContext(options))
//… your code to test

In this section I also talked about the QueryClientEvaluationWarning, which is replaced in EF Core with a hard exception if the query can’t be translated to single, valid database access command.

NOTE: My EfCore.TestSupport library has methods to setup Sqlite in-memory and SQL Server database with logging – see this documentation page for examples of how to do this.

Pages 347 to 328 Allowing too much of a data query to be moved into the software side

With EF Core’s much stringent approach to query translation any use of client vs server evaluation will cause an exception. That means that “Allowing too much of a data query to be moved into the software side” can’t happen, but I do suggest using unit tests to check your queries so that you don’t get exceptions in your live application.

Chapter 13 – A worked example of performance tuning

I plan to write a separate article to look at EF Core performance, but in summary EF Core 3 is better at handling relationships that are collections, e.g. one-to-many and many-to-many, because it loads the collections with the primary data. But there are still areas where you can improve EF Core’s performance.

NOTE: I wrote an article “Entity Framework Core performance tuning – a worked example” soon after I finished the book which summarises chapter 13. I plan to write a new article to look at the performance tuning that EF Core can offer.

Chapter 14 – Different database types of EF Core services

Pages 403 to 405 – Finding book view changes

In table 14.1 I show the BookId and ReviewId having a temporary key values after a context.Add method has been executed. In EF Core 2 these temporary key values were put in the actual key properties, but in EF Core 2 the temporary values are stored in the entity’s tracking information, and leaves the key properties unchanged. To find out why see this explanation.

Pages 413 to 416 – Replacing an EF Core service with your own modified service

This section is out of date because the internals of EF Core 3 has changed a lot. The concept is still valid but the actual examples don’t work any more as the names or signature of the methods have changed.

NOTE: I have updated my EfSchemaCompare feature in my EfCore.TestSupport library. This calls part of the scaffolder feature in EF Core to read the model of the database we are checking. You can find the EF Core 2 and EF Core 3 code that creates the DatabaseModel service that reads the database’s Schema.

Chapter 15 – Unit testing EF Core applications

Simulating a database—using an in-memory database (pages 430 to 431)

In listing 15.7 I talk about setting the about the QueryClientEvaluationWarning to catch poor performing queries. This isn’t needed any more as the new query transaction always throws an exception if the query can’t be translated to single, valid database access command.

Capturing EF Core logging information in unit testing (pages 443 to 446)

Just as I pointed out in Chapter 12, the way to capturing EF Core logging information has changed (see the Chapter 12 sub-section for the corrected code)

NOTE: I recommend using the EfCore.TestSupport NuGet package which was built as part of Chapter 15. I have updated this library to handle both EF Core >=2.1 and  EF Core >=3.0. This library has various …CreateOptionsWithLogging methods that set up options with logging included – see the docs for these methods for an example of how this works.

Extra information

One of the big (biggest?) changes in EF Core 3 is the support of the NoSQL database, Cosmos DB. This is a big development and means EF Core can now support both SQL and NoSQL databases. I expect to see more NoSQL database providers in the future.

In Chapter 14 I built a CQRS architecture system using two databases: SQL Server for all writes and some reads and a NoSQL database (RavenDb) for the high-performance read side. I plan to write a new article doing the same thing, but using EF Core 3 and Cosmos DB. 

NOTE: I already wrote an article “Building a robust CQRS database with EF Core and Cosmos DB” when the first version of the Cosmos DB database provider was out in EF Core 2.2. It worked, but was very limited and slow. I plan to write a new version with EF Core and look at the performance.

List of EF Core 3 updates to the EfCoreInAction repo

I have updated three branches of the companion EfCoreInAction repo. They are:

  • Chapter01-NetCore3: This is useful for people who want to start with a simple application. It now runs a netcoreapp3.0 console application with EF Core 3.0
  • Chapter05- NetCore3: This provides you with a ASP.NET Core 3.0/EF Core 3.0 application and the related unit tests.
  • Chapter13-Part3-NetCore3: I mainly did this to get all the unit tests from chapter 2 to 13 updated to EF Core 3. That way I could ensure I hadn’t missed any changes.

NOTE: If you have Visual Studio Code (VSCode) then there is a great extension called GitLens which allows you to compare GIT branches. You might like to use that to compare the Chapter13-Part3-NetCore21 and Chapter13-Part3-NetCore3 to see for yourself what has changed.

Conclusion

As you have seen the EF Core 3 update hardly changes the way to write your code, but there are very big changes inside. Looking at the breaking changes I can see the reasons for all the changes and quite a few of the changes were things I had bumped up against. Overall, I think updating an application from EF Core 2 to EF Core 3 shouldn’t be that difficult from the coding side.

The part that could catch you out is the query overhaul. This could cause some of your queries to fail with an exception. If you already checked for QueryClientEvaluationWarning in EF Core 2 you should be OK but if you didn’t then EF Core 3 will throw an exception on any queries that don’t translate properly to database commands.

You can get around the exception by breaking the query into two parts and using AsEnumerable, see this example, and the exception message provides information on what part of the query is causing a problem. The up side of this change is you now know none of your queries are running parts of the query in your application instead of in the database.

Opening up EF Core to NoSQL is potentially an even bigger change, as NoSQL databases are a very important part of the database scene. In Chapter 14 of my book I used a CQRS database architecture with NoSQL database (RavenDb) for the read-side database, which significantly improved the performance of my application (see this article). Please do look out for the article “Building a robust CQRS database with EF Core and Cosmos DB – NET Core 3” when it comes out, which repeat this approach but using Cosmos DB via EF Core 3.

Happy coding!

If you have a ASP.NET Core or Entity Framework Core problem that you want help on then I am available as a freelance contractor. Please send me a contact request via my Contact page and we can talk some more on Skype.