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.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.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;
CREATE TABLE [Groups] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
CONSTRAINT [PK_Groups] PRIMARY KEY ([Id])
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
CREATE INDEX [IX_Members_GroupId] ON [Members] ([GroupId]);
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20200910194835_One', N'6.0.0-alpha.1.20460.2');
COMMIT;
BEGIN TRANSACTION;
EXEC sp_rename N'[Groups].[Name]', N'GroupName', N'COLUMN';
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20200910195234_Two', N'6.0.0-alpha.1.20460.2');
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
Share
Author
Principal Program Manager - .NET Web Frameworks
Jeremy is a Principal Program Manager for .NET Web Frameworks at Microsoft. Jeremy wrote his first program in 1982, was recognized in the "who's who in Quake" list for programming the first implementation of "Midnight Capture the Flag" in Quake C and has been developing enterprise applications for 25 years with a primary focus on web-based delivery of line of business applications. Jeremy is the author of four technology books, a former 8-year Microsoft MVP for Developer Tools and Technologies, ...
More about author
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.
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.
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.
CREATE TABLE [dbo].[ArticleToArticleGroup](
[ArticleGroupID] [int] NOT NULL,
[ArticleID] [int] NOT NULL,
CONSTRAINT [PK_ArticleToArticleGroup] PRIMARY KEY CLUSTERED
[ArticleGroupID] ASC,
[ArticleID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[ArticleToArticleGroup] WITH CHECK ADD CONSTRAINT [FK_ArticleToArticleGroup_Article] FOREIGN KEY([ArticleID])
REFERENCES [dbo].[Article] ([ID])
ALTER TABLE [dbo].[ArticleToArticleGroup] CHECK CONSTRAINT [FK_ArticleToArticleGroup_Article]
ALTER TABLE [dbo].[ArticleToArticleGroup] WITH CHECK ADD CONSTRAINT [FK_ArticleToArticleGroup_ArticleGroup] FOREIGN KEY([ArticleGroupID])
REFERENCES [dbo].[ArticleGroup] ([ID])
ALTER TABLE [dbo].[ArticleToArticleGroup] CHECK CONSTRAINT [FK_ArticleToArticleGroup_ArticleGroup]
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
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