Announcing Entity Framework Core 5.0 Preview 1

Avatar

Arthur

Today we are excited to announce the first preview release of EF Core 5.0.

Prerequisites

The previews of EF Core 5.0 require .NET Standard 2.1. This means:

  • EF Core 5.0 runs on .NET Core 3.1; it does not require .NET 5.
    • This may change in future previews depending on how the plan for .NET 5 evolves.
  • EF Core 5.0 runs on other platforms that support .NET Standard 2.1.
  • EF Core 5.0 will not run on .NET Standard 2.0 platforms, including .NET Framework.

How to get EF Core 5.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 5.0.0-preview.2.20120.8

The EF Core packages published today are:

We have also published the 5.0 preview 1 release of the Microsoft.Data.Sqlite.Core ADO.NET provider.

Installing dotnet ef

As with EF Core 3.0 and 3.1, the dotnet ef command-line tool is no longer included in the .NET Core SDK. Before you can execute EF Core migration or scaffolding commands, you’ll have to install this package as either a global or local tool.

To install the preview tool globally, first uninstall any existing version with:

dotnet tool uninstall --global dotnet-ef

Then install with:

dotnet tool install --global dotnet-ef --version 5.0.0-preview.2.20120.8

It’s possible to use this new version of dotnet ef with projects that use older versions of the EF Core runtime.


Package version numbers

There was a mistake in the .NET 5 build process that resulted in the EF preview 1 packages being erroneously branded as “5.0.0-preview.2.20120.8”.

This should not have any functional impact. It should also not impact the real Preview 2, which is still planned for later in the year.


What’s new in EF Core 5 Preview 1

We maintain documentation covering new features introduced into each preview.

Some of the highlights from preview 1 are called out below.

Simple Logging

This feature is functionally similar to Database.Log in EF6. That is, it provides a simple way to get logs from EF Core without the need to configure any kind of external logging framework.

EF Core replaces Database.Log with a LogTo method called on DbContextOptionsBuilder in either AddDbContext or OnConfiguring. For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(Console.WriteLine);

Overloads exist to:

  • Set the minimum log level
    • Example: .LogTo(Console.WriteLine, LogLevel.Information)
  • Filter for only specific events:
    • Example: .LogTo(Console.WriteLine, new[] {CoreEventId.ContextInitialized, RelationalEventId.CommandExecuted})
  • Filter for all events in specific categories:
    • Example: .LogTo(Console.WriteLine, new[] {DbLoggerCategory.Database.Name}, LogLevel.Information)
  • Use a custom filter over event and level:
    • Example: .LogTo(Console.WriteLine, (id, level) => id == RelationalEventId.CommandExecuting)

Output format can be minimally configured (API is in flux) but the default output looks something like:

warn: 12/5/2019 09:57:47.574 CoreEventId.SensitiveDataLoggingEnabledWarning[10400] (Microsoft.EntityFrameworkCore.Infrastructure)
      Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data, this mode should only be enabled during development.
dbug: 12/5/2019 09:57:47.581 CoreEventId.ShadowPropertyCreated[10600] (Microsoft.EntityFrameworkCore.Model.Validation)
      The property 'BlogId' on entity type 'Post' was created in shadow state because there are no eligible CLR members with a matching name.
info: 12/5/2019 09:57:47.618 CoreEventId.ContextInitialized[10403] (Microsoft.EntityFrameworkCore.Infrastructure)
      Entity Framework Core 5.0.0-dev initialized 'BloggingContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: SensitiveDataLoggingEnabled
dbug: 12/5/2019 09:57:47.644 CoreEventId.ValueGenerated[10808] (Microsoft.EntityFrameworkCore.ChangeTracking)
      'BloggingContext' generated temporary value '-2147482647' for the 'Id' property of new 'Blog' entity.
...

Simple way to get generated SQL

EF Core 5.0 introduces the ToQueryString extension method which will return the SQL that EF Core will generate when executing a LINQ query. For example, the code:

var query = context.Set<customer>().Where(c => c.City == city);
Console.WriteLine(query.ToQueryString())

results in this output when using the SQL Server database provider:

DECLARE p0 nvarchar(4000) = N'London';

SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE [c].[City] = @__city_0

Notice that declarations for parameters of the correct type are also included in the output. This allows copy/pasting to SQL Server Management Studio, or similar tools, such that the query can be executed for debugging/analysis.

Use a C# attribute to indicate that an entity has no key

An entity type can now be configured as having no key using the new KeylessAttribute. For example:

[Keyless]
public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public int Zip { get; set; }
}

Connection or connection string can be changed on initialized DbContext

It is now easier to create a DbContext instance without any connection or connection string. Also, the connection or connection string can now be mutated on the context instance. This allows the same context instance to dynamically connect to different databases.

Change-tracking proxies

EF Core can now generate runtime proxies that automatically implement INotifyPropertyChanging and INotifyPropertyChanged. These then report value changes on entity properties directly to EF Core, avoiding the need to scan for changes. However, proxies come with their own set of limitations, so they are not for everyone.

Enhanced debug views

Debug views are an easy way to look at the internals of EF Core when debugging issues. A debug view for the Model was implemented some time ago. For EF Core 5.0, we have made the model view easier to read and added a new debug view for tracked entities in the state manager.

Model debug view

Drill down into the Model property of the DbContext in your debugger of choice and expand the DebugView property.

image

The LongView is the model view we have had for some time. The ShortView is new and doesn’t include model annotations, which make it much easier to read. For example, here is one of our test models:

Model: 
  EntityType: Chassis
    Properties: 
      TeamId (int) Required PK FK AfterSave:Throw
      Name (string)
      Version (no field, byte[]) Shadow Concurrency BeforeSave:Ignore AfterSave:Ignore ValueGenerated.OnAddOrUpdate
    Navigations: 
      Team (_team, Team) ToPrincipal Team Inverse: Chassis PropertyAccessMode.Field
    Keys: 
      TeamId PK
    Foreign keys: 
      Chassis {'TeamId'} -> Team {'Id'} Unique ToDependent: Chassis ToPrincipal: Team
  EntityType: Driver
    Properties: 
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      CarNumber (Nullable<int>)
      Championships (int) Required
      Discriminator (no field, string) Shadow Required
      FastestLaps (int) Required
      Name (string)
      Podiums (int) Required
      Poles (int) Required
      Races (int) Required
      TeamId (int) Required FK Index
      Version (no field, byte[]) Shadow Concurrency BeforeSave:Ignore AfterSave:Ignore ValueGenerated.OnAddOrUpdate
      Wins (int) Required
    Navigations: 
      Team (_team, Team) ToPrincipal Team Inverse: Drivers PropertyAccessMode.Field
    Keys: 
      Id PK
    Foreign keys: 
      Driver {'TeamId'} -> Team {'Id'} ToDependent: Drivers ToPrincipal: Team
    Indexes: 
      TeamId
  EntityType: Engine
    Properties: 
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      EngineSupplierId (int) Required FK Index Concurrency
      Name (string) Concurrency
    Navigations: 
      EngineSupplier (_engineSupplier, EngineSupplier) ToPrincipal EngineSupplier Inverse: Engines PropertyAccessMode.Field
      Gearboxes (_gearboxes, ICollection<gearbox>) Collection ToDependent Gearbox PropertyAccessMode.Field
      StorageLocation (Location) ToDependent Location PropertyAccessMode.Field
      Teams (_teams, ICollection<team>) Collection ToDependent Team Inverse: Engine PropertyAccessMode.Field
    Keys: 
      Id PK
    Foreign keys: 
      Engine {'EngineSupplierId'} -> EngineSupplier {'Id'} ToDependent: Engines ToPrincipal: EngineSupplier
    Indexes: 
      EngineSupplierId
  EntityType: EngineSupplier
    Properties: 
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      Name (string)
    Navigations: 
      Engines (_engines, ICollection<engine>) Collection ToDependent Engine Inverse: EngineSupplier PropertyAccessMode.Field
    Keys: 
      Id PK
  EntityType: Gearbox
    Properties: 
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      EngineId (no field, Nullable<int>) Shadow FK Index
      Name (string)
    Keys: 
      Id PK
    Foreign keys: 
      Gearbox {'EngineId'} -> Engine {'Id'} ToDependent: Gearboxes
    Indexes: 
      EngineId
  EntityType: Location
    Properties: 
      EngineId (no field, int) Shadow Required PK FK AfterSave:Throw ValueGenerated.OnAdd
      Latitude (double) Required Concurrency
      Longitude (double) Required Concurrency
    Keys: 
      EngineId PK
    Foreign keys: 
      Location {'EngineId'} -> Engine {'Id'} Unique Ownership ToDependent: StorageLocation
  EntityType: Sponsor
    Properties: 
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      ClientToken (no field, Nullable</int><int>) Shadow Concurrency
      Discriminator (no field, string) Shadow Required
      Name (string)
      Version (no field, byte[]) Shadow Concurrency BeforeSave:Ignore AfterSave:Ignore ValueGenerated.OnAddOrUpdate
    Keys: 
      Id PK
  EntityType: SponsorDetails
    Properties: 
      TitleSponsorId (no field, int) Shadow Required PK FK AfterSave:Throw ValueGenerated.OnAdd
      ClientToken (no field, Nullable</int><int>) Shadow Concurrency
      Days (int) Required
      Space (decimal) Required
      Version (no field, byte[]) Shadow Concurrency BeforeSave:Ignore AfterSave:Ignore ValueGenerated.OnAddOrUpdate
    Keys: 
      TitleSponsorId PK
    Foreign keys: 
      SponsorDetails {'TitleSponsorId'} -> TitleSponsor {'Id'} Unique Ownership ToDependent: Details
  EntityType: Team
    Properties: 
      Id (int) Required PK AfterSave:Throw
      Constructor (string)
      ConstructorsChampionships (int) Required
      DriversChampionships (int) Required
      EngineId (no field, Nullable</int><int>) Shadow FK Index
      FastestLaps (int) Required
      GearboxId (Nullable</int><int>) FK Index
      Name (string)
      Poles (int) Required
      Principal (string)
      Races (int) Required
      Tire (string)
      Version (no field, byte[]) Shadow Concurrency BeforeSave:Ignore AfterSave:Ignore ValueGenerated.OnAddOrUpdate
      Victories (int) Required
    Navigations: 
      Chassis (_chassis, Chassis) ToDependent Chassis Inverse: Team PropertyAccessMode.Field
      Drivers (_drivers, ICollection<driver>) Collection ToDependent Driver Inverse: Team PropertyAccessMode.Field
      Engine (_engine, Engine) ToPrincipal Engine Inverse: Teams PropertyAccessMode.Field
      Gearbox (_gearbox, Gearbox) ToPrincipal Gearbox PropertyAccessMode.Field
    Keys: 
      Id PK
    Foreign keys: 
      Team {'EngineId'} -> Engine {'Id'} ToDependent: Teams ToPrincipal: Engine
      Team {'GearboxId'} -> Gearbox {'Id'} Unique ToPrincipal: Gearbox
    Indexes: 
      EngineId
      GearboxId Unique
  EntityType: TestDriver Base: Driver
  EntityType: TitleSponsor Base: Sponsor
    Navigations: 
      Details (_details, SponsorDetails) ToDependent SponsorDetails PropertyAccessMode.Field

State manager debug view

The state manager is a little mode hidden than the model. To find it, drill down into the ChangeTracker property of the DbContext in your debugger of choice and then look in the StateManager property and expand the DebugView.

image

The short view of the state manager shows:

  • Each entity being tracked
  • Its primary key value(s)
  • The entity state – one of Added, Unchanged, Modified, or Deleted.
  • The values of an foreign key properties

For example:

Engine (Shared) {Id: 1} Unchanged FK {EngineSupplierId: 1}
Location (Shared) {EngineId: 1} Unchanged FK {EngineId: 1}
Team (Shared) {Id: 4} Modified FK {EngineId: 1} FK {GearboxId: <null>}

The long view shows everything in the short view together with:

  • The current value of each property
  • Whether or not the property is marked as modified
  • The original value of the property, if different from the current value
  • The entity referenced by a reference navigation using the referenced entity’s primary key value
  • The list if entities referenced by a collection navigation, again using primary key values

For example:

Engine (Shared) {Id: 1} Unchanged
  Id: 1 PK
  EngineSupplierId: 1 FK
  Name: 'FO 108X'
  EngineSupplier: <null>
  Gearboxes: </null><null>
  StorageLocation: {EngineId: 1}
  Teams: [{Id: 4}]
Location (Shared) {EngineId: 1} Unchanged
  EngineId: 1 PK FK
  Latitude: 47.64491
  Longitude: -122.128101
Team (Shared) {Id: 4} Modified
  Id: 4 PK
  Constructor: 'Ferrari'
  ConstructorsChampionships: 16
  DriversChampionships: 15
  EngineId: 1 FK Modified Originally 3
  FastestLaps: 221
  GearboxId: </null><null> FK
  Name: 'Scuderia Ferrari Marlboro'
  Poles: 203
  Principal: 'Stefano Domenicali'
  Races: 805
  Tire: 'Bridgestone'
  Version: '0x000000000001405A'
  Victories: 212
  Chassis: </null><null>
  Drivers: []
  Engine: {Id: 1}
  Gearbox: </null><null>

Improved handling of database null semantics

Relational databases typically treat NULL as an unknown value and therefore not equal to any other NULL. C#, on the other hand, treats null as a defined value which compares equal to any other null. EF Core by default translates queries so that they use C# null semantics. EF Core 5.0 greatly improves the efficiency of these translations.

Indexer properties

EF Core 5.0 supports mapping of C# indexer properties. This allows entities to act as property bags where columns are mapped to named properties in the bag.

Generation of check constraints for enum mappings

EF Core 5.0 Migrations can now generate CHECK constraints for enum property mappings. For example:

EnumColumn VARCHAR(10) NOT NULL CHECK (MyEnumColumn IN('Useful', 'Useless', 'Unknown'))

IsRelational

A new IsRelational method has been added in addition to the existing IsSqlServer, IsSqlite, and IsInMemory. This can be used to test if the DbContext is using any relational database provider. For example:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    if (Database.IsRelational())
    {
        // Do relational-specific model configuration.
    }
}

Cosmos optimistic concurrency with ETags

The Azure Cosmos DB database provider now supports optimistic concurrency using ETags. Use the model builder in OnModelCreating to configure an ETag:

builder.Entity&lt;customer>().Property(c => c.ETag).IsEtagConcurrency();

SaveChanges will then throw an DbUpdateConcurrencyException on a concurrency conflict, which can be handled to implement retries, etc.

Query translations for more DateTime constructs

Queries containing new DateTime construction are now translated.

In addition, the following SQL Server functions are now mapped: * DateDiffWeek * DateFromParts

For example:

var count = context.Orders.Count(c => date > EF.Functions.DateFromParts(DateTime.Now.Year, 12, 25));

Query translations for more byte array constructs

Queries using Contains, Length, SequenceEqual, etc. on byte[] properties are now translated to SQL. For example:

var blogs = context.Blogs.Where(e => e.Picture.Contains((byte)127)).ToList();

Query translation for Reverse

Queries using Reverse are now translated. For example:

context.Employees.OrderBy(e => e.EmployeeID).Reverse()

Query translation for bitwise operators

Queries using bitwise operators are now translated in more cases. For example:

context.Orders.Where(o => ~o.OrderID == negatedId)

Query translation for strings on Cosmos

Queries that use the string methods Contains, StartsWith, and EndsWith are now translated when using the Azure Cosmos DB provider.


Daily builds

EF Core previews are aligned with .NET 5 previews. These previews tend to lag behind the latest work on EF Core. Consider using the daily builds instead to get the most up-to-date EF Core features and bug fixes.

As with the previews, the daily builds do not require .NET 5; they can be used with GA/RTM release of .NET Core 3.1.


Documentation and feedback

The starting point for all EF Core documentation is docs.microsoft.com/ef/core/.

Please file issues found and any other feedback on the dotnet/efcore GitHub repo.


Thank you from the team!

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

ajcvickers
Arthur Vickers
AndriySvyryd
Andriy Svyryd

Brice Lambson
JeremyLikness
Jeremy Likness
lajones
lajones
maumar
Maurycy Markowski
roji
Shay Rojansky
smitpatel
Smit Patel

Thank you to our contributors!

A big thank you to the following community members who have already contributed code or documentation to the EF Core 5 release!

aevitas
aevitas
alaatm
Alaa Masoud
aleksandar-manukov
Aleksandar Manukov
amrbadawy
Amr Badawy
AnthonyMonterrosa
Anthony Monterrosa
bbrandt
Ben Brandt
benmccallum
Ben McCallum
ccjx
Clarence Cai
CGijbels
Christophe Gijbels
cincuranet
Jiri Cincura
Costo
Vincent Costel
dshuvaev
Dmitry Shuvaev
EricStG
Eric St-Georges
ErikEJ
Erik Ejlskov Jensen
gravbox
Christopher Davis
ivaylokenov
Ivaylo Kenov
jfoshee
Jacob Foshee
jmzagorski
Jeremy Zagorski
jviau
Jacob Viau
knom
Max K.
lohoris-crane
lohoris-crane
loic-sharma
Loïc Sharma
lokalmatador
lokalmatador
mariusGundersen
Marius Gundersen
Marusyk
Roman Marusyk
matthiaslischka
Matthias Lischka
MaxG117
MaxG117
MHDuke
MHDuke
mikes-gh
Mike Surcouf
Muppets
Neil Bostrom
nmichels
Nícolas Michels
OOberoi
Obi Oberoi
orionstudt
Josh Studt
ozantopal
Ozan Topal
pmiddleton
Paul Middleton
prog-rajkamal
Raj
ptjhuang
Peter Huang
ralmsdeveloper
Rafael Almeida Santos
redoz
Patrik Husfloen
rmarskell
Richard Marskell
sguitardude
sguitardude
SimpleSamples
Sam Hobbs
svengeance
Sven
VladDragnea
Vlad
vslee
vslee
WeihanLi
liweihan
Youssef1313
Youssef Victor

14 comments

Leave a comment