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:
- Microsoft.EntityFrameworkCore – The main EF Core package
- Microsoft.EntityFrameworkCore.SqlServer – Database provider for Microsoft SQL Server and SQL Azure
- Microsoft.EntityFrameworkCore.Sqlite – Database provider for SQLite
- 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
- Microsoft.EntityFrameworkCore.Design – Shared design-time components for EF Core tools
- Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite – SQL Server support for spatial types
- Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite – SQLite support for spatial types
- Microsoft.EntityFrameworkCore.Proxies – Lazy-loading and change-tracking proxies
- Microsoft.EntityFrameworkCore.Abstractions – Decoupled EF Core abstractions
- Microsoft.EntityFrameworkCore.Relational – Shared EF Core components for relational database providers
- Microsoft.EntityFrameworkCore.Analyzers – C# analyzers for EF Core
- Microsoft.EntityFrameworkCore.Sqlite.Core – Database provider for SQLite without a packaged native binary
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)
- Example:
- Filter for only specific events:
- Example:
.LogTo(Console.WriteLine, new[] {CoreEventId.ContextInitialized, RelationalEventId.CommandExecuted})
- Example:
- Filter for all events in specific categories:
- Example:
.LogTo(Console.WriteLine, new[] {DbLoggerCategory.Database.Name}, LogLevel.Information)
- Example:
- Use a custom filter over event and level:
- Example:
.LogTo(Console.WriteLine, (id, level) => id == RelationalEventId.CommandExecuting)
- Example:
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.
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
.
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<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!
Arthur Vickers |
Andriy Svyryd |
Brice Lambson |
Jeremy Likness |
lajones |
Maurycy Markowski |
Shay Rojansky |
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!
Is there documentation on how to mutate the connection string on the db context?
EF Core rocks. Keep up the good work 🙂
When is UWP support coming?
We’re working with our management to understand the story for EF Core and UWP.
Would you recommend this to be used in production code?
I wouldn't recommend or non-recommend it. On the one hand, it is not officially supported with a go-live license. On the other hand, if you have confidence in your testing and your application is running without errors, then from what I know personally of the code quality of the preview, I would probably be okay with it. We've certainly talked to happy customers who are running previews or daily builds in production. But understand that...
Looks like a lot of cool new stuff. Too bad, after unscrewing over .NET Framework developers by switching EF Core 3.1 back to .NET Standard 2.0, we are now back to getting screwed over again. Thanks again Microsoft! And thanks for not bringing Web Forms forward into .NET 5. ASP.NET Core is a train wreck IMHO.
I understand your frustration. However, the overall strategy for .NET 5 is bigger than EF Core and, while we have input into it, is largely out of the hands of the EF Core team. You may get more traction with your comments by giving this feedback at the .NET level rather than on the EF Core release. That being said, it's not feasible to bring everything into the .NET 5 world and my understanding is...
I think it’s ridiculous. Every major new version of EF has tons of breaking changes. The query engine is completely changed around. I just upgraded from EF Core 2.x to EF Core 3.1 and was working through the breaking changes there. Now I find that it was a complete waste of time as support for the latest version is being dropped anyway.
Please consider supporting filtered includes.
Like
“` C#
.Include(x => x.People.Where(x => x.Age > 18))
“`
For example. Also would love to see this supported in .net standard 2.0
Filtered Include is being worked on and will be in a future preview.
Huge thanks for the Generation of check constraints for enum mappings feature. I loved this a lot. You people are doing fantastic works. I shall give you a million thanks if you implement the following feature also. This is one of the most wanted features of EF Core.
https://github.com/dotnet/efcore/issues/10862
Hi, I really appreciate your work. Thanks.
If we implement INotifyPropertyChanged or INotifyPropertyChaniging manually or using something like fody, can we achieve its benefits without using proxies?
Yes. This has actually been supported since EF Core 1.1, but we need better docs. See https://blog.oneunicorn.com/2016/11/16/notification-entities-in-ef-core-1-1/