Today, the Entity Framework Core team announces the first release candidate (RC1) of EF Core 5.0. This is a feature complete release candidate of EF Core 5.0 and ships with a "go live" license. You are supported using it in production. This is a great opportunity to start using EF Core 5.0 early while there is still time to fix remaining issues. We're looking for reports of any remaining critical bugs that should be fixed before the final release.
This release includes new features like many-to-many, property bags, event counters, required 1:1 dependents and the ability to intercept SaveChanges
and listen to save events. It also includes improvements to model-building, migrations, and more. A more in-depth look at what's new follows later in this blog post.
Prerequisites
EF Core 5.0 will not run on .NET Standard 2.0 platforms, including .NET Framework.
- The release candidates of EF Core 5.0 require .NET Standard 2.1.
- This means that EF Core 5.0 will run on .NET Core 3.1 and does not require .NET 5.
To summarize: EF Core 5.0 runs on platforms that support .NET Standard 2.1.
How to get EF Core 5.0 Release Candidate 1
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-rc.1.20451.13
This following table links to the RC1 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.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 |
Installing the EF Core Command Line Interface (CLI)
As with EF Core 3.0 and 3.1, the EF Core CLI 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 RC1 tool globally the first time, use:
dotnet tool install --global dotnet-ef --version 5.0.0-rc.1.20451.13
If you already have the tool installed, update it with:
dotnet tool update --global dotnet-ef --version 5.0.0-rc.1.20451.13
It’s possible to use this new version of the EF Core CLI with projects that use older versions of the EF Core runtime.
What's New in EF Core 5 RC1
We maintain documentation covering new features introduced into each release.
Some of the highlights from RC1 are called out below. This release candidate also includes several bug fixes.
Many-to-many
EF Core 5.0 supports many-to-many relationships without explicitly mapping the join table.
For example, consider these entity types:
public class Post
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int Id { get; set; }
public string Text { get; set; }
public ICollection<Post> Posts { get; set; }
}
Notice that Post
contains a collection of Tags
, and Tag
contains a collection of Posts
. EF Core 5.0 recognizes this as a many-to-many relationship by convention. This means no code is required in OnModelCreating
:
public class BlogContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
}
When Migrations (or EnsureCreated
) are used to create the database, EF Core will automatically create the join table. For example, on SQL Server for this model, EF Core generates:
CREATE TABLE [Posts] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id])
);
CREATE TABLE [Tag] (
[Id] int NOT NULL IDENTITY,
[Text] nvarchar(max) NULL,
CONSTRAINT [PK_Tag] PRIMARY KEY ([Id])
);
CREATE TABLE [PostTag] (
[PostsId] int NOT NULL,
[TagsId] int NOT NULL,
CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
CONSTRAINT [FK_PostTag_Posts_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_PostTag_Tag_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tag] ([Id]) ON DELETE CASCADE
);
CREATE INDEX [IX_PostTag_TagsId] ON [PostTag] ([TagsId]);
Creating and associating Blog
and Post
entities results in join table updates happening automatically. For example:
var beginnerTag = new Tag {Text = "Beginner"};
var advancedTag = new Tag {Text = "Advanced"};
var efCoreTag = new Tag {Text = "EF Core"};
context.AddRange(
new Post {Name = "EF Core 101", Tags = new List<Tag> {beginnerTag, efCoreTag}},
new Post {Name = "Writing an EF database provider", Tags = new List<Tag> {advancedTag, efCoreTag}},
new Post {Name = "Savepoints in EF Core", Tags = new List<Tag> {beginnerTag, efCoreTag}});
context.SaveChanges();
After inserting the Posts and Tags, EF will then automatically create rows in the join table. For example, on SQL Server:
SET NOCOUNT ON;
INSERT INTO [PostTag] ([PostsId], [TagsId])
VALUES (@p6, @p7),
(@p8, @p9),
(@p10, @p11),
(@p12, @p13),
(@p14, @p15),
(@p16, @p17);
For queries, Include and other query operations work just like for any other relationship. For example:
foreach (var post in context.Posts.Include(e => e.Tags))
{
Console.Write($"Post \"{post.Name}\" has tags");
foreach (var tag in post.Tags)
{
Console.Write($" '{tag.Text}'");
}
}
The SQL generated uses the join table automatically to bring back all related Tags:
SELECT [p].[Id], [p].[Name], [t0].[PostsId], [t0].[TagsId], [t0].[Id], [t0].[Text]
FROM [Posts] AS [p]
LEFT JOIN (
SELECT [p0].[PostsId], [p0].[TagsId], [t].[Id], [t].[Text]
FROM [PostTag] AS [p0]
INNER JOIN [Tag] AS [t] ON [p0].[TagsId] = [t].[Id]
) AS [t0] ON [p].[Id] = [t0].[PostsId]
ORDER BY [p].[Id], [t0].[PostsId], [t0].[TagsId], [t0].[Id]
Unlike EF6, EF Core allows full customization of the join table. For example, the code below configures a many-to-many relationship that also has navigations to the join entity, and in which the join entity contains a payload property:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Community>()
.HasMany(e => e.Members)
.WithMany(e => e.Memberships)
.UsingEntity<PersonCommunity>(
b => b.HasOne(e => e.Member).WithMany().HasForeignKey(e => e.MembersId),
b => b.HasOne(e => e.Membership).WithMany().HasForeignKey(e => e.MembershipsId))
.Property(e => e.MemberSince).HasDefaultValueSql("CURRENT_TIMESTAMP");
}
Map entity types to queries
Entity types are commonly mapped to tables or views such that EF Core will pull back the contents of the table or view when querying for that type. EF Core 5.0 allows an entity type to mapped to a "defining query". (This was partially supported in previous versions, but is much improved and has different syntax in EF Core 5.0.)
For example, consider two tables; one with modern posts; the other with legacy posts. The modern posts table has some additional columns, but for the purpose of our application we want both modern and legacy posts tp be combined and mapped to an entity type with all necessary properties:
public class Post
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
In EF Core 5.0, ToSqlQuery
can be used to map this entity type to a query that pulls and combines rows from both tables:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>().ToSqlQuery(
@"SELECT Id, Name, Category, BlogId FROM posts
UNION ALL
SELECT Id, Name, ""Legacy"", BlogId from legacy_posts");
}
Notice that the legacy_posts
table does not have a Category
column, so we instead synthesize a default value for all legacy posts.
This entity type can then be used in the normal way for LINQ queries. For example. the LINQ query:
var posts = context.Posts.Where(e => e.Blog.Name.Contains("Unicorn")).ToList();
Generates the following SQL on SQLite:
SELECT "p"."Id", "p"."BlogId", "p"."Category", "p"."Name"
FROM (
SELECT Id, Name, Category, BlogId FROM posts
UNION ALL
SELECT Id, Name, "Legacy", BlogId from legacy_posts
) AS "p"
INNER JOIN "Blogs" AS "b" ON "p"."BlogId" = "b"."Id"
WHERE ('Unicorn' = '') OR (instr("b"."Name", 'Unicorn') > 0)
Notice that the query configured for the entity type is used as a starting for composing the full LINQ query.
Event counters
.NET event counters are a way to efficiently expose performance metrics from an application. EF Core 5.0 includes event counters under the Microsoft.EntityFrameworkCore
category. For example:
dotnet counters monitor Microsoft.EntityFrameworkCore -p 49496
This tells dotnet counters to start collecting EF Core events for process 49496. This generates output like this in the console:
[Microsoft.EntityFrameworkCore]
Active DbContexts 1
Execution Strategy Operation Failures (Count / 1 sec) 0
Execution Strategy Operation Failures (Total) 0
Optimistic Concurrency Failures (Count / 1 sec) 0
Optimistic Concurrency Failures (Total) 0
Queries (Count / 1 sec) 1,755
Queries (Total) 98,402
Query Cache Hit Rate (%) 100
SaveChanges (Count / 1 sec) 0
SaveChanges (Total) 1
Property bags
EF Core 5.0 allows the same CLR type to be mapped to multiple different entity types. Such types are known as shared-type entity types. This feature combined with indexer properties (included in preview 1) allows property bags to be used as entity type.
For example, the DbContext below configures the BCL type Dictionary<string, object>
as a shared-type entity type for both products and categories.
public class ProductsContext : DbContext
{
public DbSet<Dictionary<string, object>> Products => Set<Dictionary<string, object>>("Product");
public DbSet<Dictionary<string, object>> Categories => Set<Dictionary<string, object>>("Category");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.SharedTypeEntity<Dictionary<string, object>>("Category", b =>
{
b.IndexerProperty<string>("Description");
b.IndexerProperty<int>("Id");
b.IndexerProperty<string>("Name").IsRequired();
});
modelBuilder.SharedTypeEntity<Dictionary<string, object>>("Product", b =>
{
b.IndexerProperty<int>("Id");
b.IndexerProperty<string>("Name").IsRequired();
b.IndexerProperty<string>("Description");
b.IndexerProperty<decimal>("Price");
b.IndexerProperty<int?>("CategoryId");
b.HasOne("Category", null).WithMany();
});
}
}
Dictionary objects ("property bags") can now be added to the context as entity instances and saved. For example:
var beverages = new Dictionary<string, object>
{
["Name"] = "Beverages",
["Description"] = "Stuff to sip on"
};
context.Categories.Add(beverages);
context.SaveChanges();
These entities can then be queried and updated in the normal way:
var foods = context.Categories.Single(e => e["Name"] == "Foods");
var marmite = context.Products.Single(e => e["Name"] == "Marmite");
marmite["CategoryId"] = foods["Id"];
marmite["Description"] = "Yummy when spread _thinly_ on buttered Toast!";
context.SaveChanges();
SaveChanges interception and events
EF Core 5.0 introduces both .NET events and an EF Core interceptor triggered when SaveChanges is called.
The events are simple to use; for example:
context.SavingChanges += (sender, args) =>
{
Console.WriteLine($"Saving changes for {((DbContext)sender).Database.GetConnectionString()}");
};
context.SavedChanges += (sender, args) =>
{
Console.WriteLine($"Saved {args.EntitiesSavedCount} changes for {((DbContext)sender).Database.GetConnectionString()}");
};
Notice that:
- The event sender is the
DbContext
instance - The args for the
SavedChanges
event contains the number of entities saved to the database
The interceptor is defined by ISaveChangesInterceptor
, but it is often convienient to inherit from SaveChangesInterceptor
to avoid implementing every method. For example:
public class MySaveChangesInterceptor : SaveChangesInterceptor
{
public override InterceptionResult<int> SavingChanges(
DbContextEventData eventData,
InterceptionResult<int> result)
{
Console.WriteLine($"Saving changes for {eventData.Context.Database.GetConnectionString()}");
return result;
}
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData,
InterceptionResult<int> result,
CancellationToken cancellationToken = new CancellationToken())
{
Console.WriteLine($"Saving changes asynchronously for {eventData.Context.Database.GetConnectionString()}");
return new ValueTask<InterceptionResult<int>>(result);
}
}
Notice that:
- The interceptor has both sync and async methods. This can be useful if you need to perform async I/O, such as writing to an audit server.
- The interceptor allows SaveChanges to be skipped using the
InterceptionResult
mechanism common to all interceptors.
The downside of interceptors is that they must be registered on the DbContext when it is being constructed. For example:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.AddInterceptors(new MySaveChangesInterceptor())
.UseSqlite("Data Source = test.db");
In contrast, the events can be registered on the DbContext instance at any time.
Exclude tables from migrations
It is sometimes useful to have a single entity type mapped in multiple DbContexts. This is especially true when using bounded contexts, for which it is common to have a different DbContext type for each bounded context.
For example, a User
type may be needed by both an authorization context and a reporting context. If a change is made to the User
type, then migrations for both DbContexts will attempt to update the database. To prevent this, the model for one of the contexts can be configured to exclude the table from its migrations.
In the code below, the AuthorizationContext
will generate migrations for changes to the Users
table, but the ReportingContext
will not, preventing the migrations from clashing.
public class AuthorizationContext : DbContext
{
public DbSet<User> Users { get; set; }
}
public class ReportingContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("Users", t => t.ExcludeFromMigrations());
}
}
Required 1:1 dependents
In EF Core 3.1, the dependent end of a one-to-one relationship was always considered optional. This was most apparent when using owned entities. For example, consider the following model and configuration:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Address HomeAddress { get; set; }
public Address WorkAddress { get; set; }
}
public class Address
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string Country { get; set; }
public string Postcode { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>(b =>
{
b.OwnsOne(e => e.HomeAddress,
b =>
{
b.Property(e => e.Line1).IsRequired();
b.Property(e => e.City).IsRequired();
b.Property(e => e.Region).IsRequired();
b.Property(e => e.Postcode).IsRequired();
});
b.OwnsOne(e => e.WorkAddress);
});
}
This results in Migrations creating the following table for SQLite:
CREATE TABLE "People" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_People" PRIMARY KEY AUTOINCREMENT,
"Name" TEXT NULL,
"HomeAddress_Line1" TEXT NULL,
"HomeAddress_Line2" TEXT NULL,
"HomeAddress_City" TEXT NULL,
"HomeAddress_Region" TEXT NULL,
"HomeAddress_Country" TEXT NULL,
"HomeAddress_Postcode" TEXT NULL,
"WorkAddress_Line1" TEXT NULL,
"WorkAddress_Line2" TEXT NULL,
"WorkAddress_City" TEXT NULL,
"WorkAddress_Region" TEXT NULL,
"WorkAddress_Country" TEXT NULL,
"WorkAddress_Postcode" TEXT NULL
);
Notice that all the columns are nullable, even though some of the HomeAddress
properties have been configured as required. Also, when querying for a Person
, if all the columns for either the home or work address are null, then EF Core will leave the HomeAddress
and/or WorkAddress
properties as null, rather than setting an empty instance of Address
.
In EF Core 5.0, the HomeAddress
navigation can now be configured as as a required dependent. For example:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>(b =>
{
b.OwnsOne(e => e.HomeAddress,
b =>
{
b.Property(e => e.Line1).IsRequired();
b.Property(e => e.City).IsRequired();
b.Property(e => e.Region).IsRequired();
b.Property(e => e.Postcode).IsRequired();
});
b.Navigation(e => e.HomeAddress).IsRequired();
b.OwnsOne(e => e.WorkAddress);
});
}
The table created by Migrations will now included non-nullable columns for the required properties of the required dependent:
CREATE TABLE "People" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_People" PRIMARY KEY AUTOINCREMENT,
"Name" TEXT NULL,
"HomeAddress_Line1" TEXT NOT NULL,
"HomeAddress_Line2" TEXT NULL,
"HomeAddress_City" TEXT NOT NULL,
"HomeAddress_Region" TEXT NOT NULL,
"HomeAddress_Country" TEXT NULL,
"HomeAddress_Postcode" TEXT NOT NULL,
"WorkAddress_Line1" TEXT NULL,
"WorkAddress_Line2" TEXT NULL,
"WorkAddress_City" TEXT NULL,
"WorkAddress_Region" TEXT NULL,
"WorkAddress_Country" TEXT NULL,
"WorkAddress_Postcode" TEXT NULL
);
In addition, EF Core will now throw an exception if an attempt is made to save an owner which has a null required dependent. In this example, EF Core will throw when attempting to save a Person
with a null HomeAddress
.
Finally, EF Core will still create an instance of a required dependent even when all the columns for the required dependent have null values.
Options for migration generation
EF Core 5.0 introduces greater control over generation of migrations for different purposes. This includes the ability to:
- Know if the migration is being generated for a script or for immediate execution
- Know if an idempotent script is being generated
- Know if the script should exclude transaction statements (See Migrations scripts with transactions below.)
This behavior is specified by an the MigrationsSqlGenerationOptions
enum, which can now be passed to IMigrator.GenerateScript
.
Also included in this work is better generation of idempotent scripts with calls to EXEC
on SQL Server when needed. This work also enables similar improvements to the scripts generated by other database providers, including PostgreSQL.
Migrations scripts with transactions
SQL scripts generated from migrations now contain statements to begin and commit transactions as appropriate for the migration. For example, the migration script below was generated from two migrations. Notice that each migration is now applied inside a transaction.
BEGIN TRANSACTION;
GO
CREATE TABLE [Groups] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
CONSTRAINT [PK_Groups] PRIMARY KEY ([Id])
);
GO
CREATE TABLE [Members] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
[GroupId] int NULL,
CONSTRAINT [PK_Members] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Members_Groups_GroupId] FOREIGN KEY ([GroupId]) REFERENCES [Groups] ([Id]) ON DELETE NO ACTION
);
GO
CREATE INDEX [IX_Members_GroupId] ON [Members] ([GroupId]);
GO
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20200910194835_One', N'6.0.0-alpha.1.20460.2');
GO
COMMIT;
GO
BEGIN TRANSACTION;
GO
EXEC sp_rename N'[Groups].[Name]', N'GroupName', N'COLUMN';
GO
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20200910195234_Two', N'6.0.0-alpha.1.20460.2');
GO
COMMIT;
As mentioned in the previous section, this use of transactions can be disabled if transactions need to be handled differently.
See pending migrations
This feature was contributed from the community by @Psypher9. Many thanks for the contribution!
The dotnet ef migrations list
command now shows which migrations have not yet been applied to the database. For example:
ajcvickers@avickers420u:~/AllTogetherNow/Daily$ dotnet ef migrations list
Build started...
Build succeeded.
20200910201647_One
20200910201708_Two
20200910202050_Three (Pending)
ajcvickers@avickers420u:~/AllTogetherNow/Daily$
In addition, there is now a Get-Migration
command for the Package Manager Console with the same functionality.
ModelBuilder API for value comparers
EF Core properties for custom mutable types require a value comparer for property changes to be detected correctly. This can now be specified as part of configuring the value conversion for the type. For example:
modelBuilder
.Entity<EntityType>()
.Property(e => e.MyProperty)
.HasConversion(
v => JsonSerializer.Serialize(v, null),
v => JsonSerializer.Deserialize<List<int>>(v, null),
new ValueComparer<List<int>>(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => c.ToList()));
EntityEntry TryGetValue methods
This feature was contributed from the community by @m4ss1m0g. Many thanks for the contribution!
A TryGetValue
method has been added to EntityEntry.CurrentValues
and EntityEntry.OriginalValues
. This allows the value of a property to be requested without first checking if the property is mapped in the EF model. For example:
if (entry.CurrentValues.TryGetValue(propertyName, out var value))
{
Console.WriteLine(value);
}
Default max batch size for SQL Server
Starting with EF Core 5.0, the default maximum batch size for SaveChanges on SQL Server is now 42. As is well known, this is also the answer to the Ultimate Question of Life, the Universe, and Everything. However, this is probably a coincidence, since the value was obtained through analysis of batching performance. We do not believe that we have discovered a form of the Ultimate Question, although it does seem somewhat plausible that the Earth was created to understand why SQL Server works the way it does.
Default environment to Development
The EF Core command line tools now automatically configure the ASPNETCORE_ENVIRONMENT
and DOTNET_ENVIRONMENT
environment variables to "Development". This brings the experience when using the generic host in line with the experience for ASP.NET Core during development. See #19903.
Better migrations column ordering
The columns for unmapped base classes are now ordered after other columns for mapped entity types. Note this only impacts newly created tables. The column order for existing tables remains unchanged. See #11314.
Query improvements
EF Core 5.0 RC1 contains some additional query translation improvements:
- Translation of
is
on Cosmos–see #16391 - User-mapped functions can now be annotated to control null propagation–see #19609
- Support for translation of GroupBy with conditional aggregates–see #11711
- Translation of Distinct operator over group element before aggregate–see #17376
Model building for fields
Finally for RC1, EF Core now allows use of the lambda methods in the ModelBuilder for fields as well as properties. For example, if you are averse to properties for some reason and decide to use public fields, then these fields can now be mapped using the lambda builders:
public class Post
{
public int Id;
public string Name;
public string Category;
public int BlogId;
public Blog Blog;
}
public class Blog
{
public int Id;
public string Name;
public ICollection<Post> Posts;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(b =>
{
b.Property(e => e.Id);
b.Property(e => e.Name);
});
modelBuilder.Entity<Post>(b =>
{
b.Property(e => e.Id);
b.Property(e => e.Name);
b.Property(e => e.Category);
b.Property(e => e.BlogId);
b.HasOne(e => e.Blog).WithMany(e => e.Posts);
});
}
While this is now possible, we are certainly not recommending that you do this. Also, note that this does not add any additional field mapping capabilities to EF Core, it only allows the lambda methods to be used instead of always requiring the string methods. This is seldom useful since fields are rarely public.
Daily builds
EF Core previews and release candidates are aligned with the .NET 5 release cycle. These releases 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. Daily builds are considered stable.
Contribute to .NET 5
The .NET documentation team is reorganizing .NET content to better match the workloads you build with .NET. This includes a new .NET Data landing page that will link out to data-related topics ranging from EF Core to APIs, Big Data, and Machine learning. The planning and execution will be done completely in the open on GitHub. This is your opportunity to help shape the hierarchy and content to best fit your needs as a .NET developer. We look forward to your contributions!
The EF Core Community Standup
The EF Core team is now live streaming every other Wednesday at 10am Pacific Time, 1pm Eastern Time, or 17:00 UTC. Join the stream to ask questions about the EF Core topic of your choice, including the latest release candidate.
- Visit the .NET Community Standup page to preview upcoming shows and view recordings from past shows
- Suggest a guest or project, including your own by posting to the linked discussion
- You can also request an EF Core demo
Documentation and Feedback
The starting point for all EF Core documentation is docs.microsoft.com/ef/.
Please file issues found and any other feedback on the dotnet/efcore GitHub repo.
Helpful Short Links
The following short links are provided for easy reference and access.
Main documentation: https://aka.ms/efdocs
Issues and feature requests for EF Core: https://aka.ms/efcorefeedback
Entity Framework Roadmap: https://aka.ms/efroadmap
What's new in EF Core 5.x? https://aka.ms/efcore5
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!
A huge "thanks" to the following community members who have already contributed code or documentation to the EF Core 5 release! (List is in chronological order of first contribution to EF Core 5).
Diego Vega |
lajones |
Please, what are the disadvantages of using EF Core 5 in .NET Core 3.1?
It should be the same experience in .NET Core 3.1 as .NET 5.
If I’m on .NET Framework 4.8 and using EF Core 3.x, am I going to run into problems with NuGet offering me incompatible updates when EF Core 5 is released? Also, what’s going to happen with regard to the version that is supported on .NET Framework 4.8 (EF Core 3.x)? Will it continue to be patched?
EF Core 3.x follows the .NET Core 3.x support timeline. You are correct that EF Core 5.0 will not work in .NET Framework projects.
When will we get back a GUI like we had in the original .net? Doing everything in command line becomes tedious.
What GUI features are you looking for?
I am interested in many-to-many and payload feature. Can this payload property be used for any kind of filtering? I have a scenario where my join table also contains From/To fields. Any join is valid in a certain period of time. Can this feature be used here for that kind of filtering without creating intermediate joining entity?
Yes, payload can be used in filtering. Depending on your configuration, you just need to determine how to get that join entity.
If there is navigation to the join entity the navigation can be used, or the DbSet of the join entity can be used in a query with a manual join.
The payload property in the join entity can be accessed via indexer (since it's implemented as a dictionary) or by using the EF.Property method.
This is GREAT news. Congratulation with a release that final can put a nail in the coffin of NHibernate (:
Noe I just have to wait for an Oracle provider.
Yeah, EF finally supports a feature that NHibernate supported probably 15 years ago, many-to-many.
Hello Jeremy
How to scaffold many-to-many relations in the existing database without join entity?
I have the Article, ArticleGroup, and ArticleToArticleGroup tables. Scaffolding generates entities for all of them. And Article entity has the 'ArticleArticleGroups' property but doesn't have 'Groups' property.
<code>
Take a look at this standup where Arthur does a deep dive into many-to-many. Let us know if it answers your question!
https://youtu.be/W1sxepfIMRM
Thank you, Jeremy. It’s a cool standup, but it doesn’t affect the “Scaffold” many-to-many relationship of the existing database.
Thanks for the clarification. This is being tracked here: https://github.com/dotnet/efcore/issues/22475
TPT + ExcludeFromMigrations and finally everything is there for my project – THANKS for your great work!
Hello Jeremy,
I’m a little bit confused concerning the Many-to-many example.
There are 2 entity types: post and tag.
But the DB Context is showing
public DbSet Posts { get; set; }
public DbSet Blogs { get; set; }
Is this right? I’m not an EF specialist, but I would expect the following:
public DbSet Posts { get; set; }
public DbSet Tags { get; set; }
Best Markus
Your expectations are good… it was a copy/paste error. It’s fixed, thank you for finding this!