EF Code First changed join table convention between version 4 and 6 - entity-framework

We're looking at upgrading our dinosaur of EF 4.2 this week to EF 6.0.2 (Code First). For the most part, aside from some namespace changes, everything works, with one exception.
Using EF 4.2, one particular many-to-many relation had a specific join table name expected. EF 6 expects a different one.
Here is the relevant portions of code
public class MyDbContext : DbContext {
/* some unrelated collections*/
public IDbSet<DBFoo> Foos { get; set; }
/* some unrelated collections */
public IDbSet<DBBar> Bars { get; set; }
/* some unrelated collections */
}
public class DBFoo {
/* DBFoo's properties */
public virtual ICollection<DBBar> Bars { get; set; }
}
public class DBBar {
/* DBBar's properties */
/*some unrelated navigation properties (some single, some collections)*/
public virtual ICollection<DBFoo> Foos { get; set; }
/*more unrelated navigation properties*/
}
It's important to note that the "unrelated" properties mentioned are other entities, but themselves have no references to "DBFoo".
Now, in EF 4.2, the join table between these two entities was expected to be DBBarDBFoos. This was without any configuration in the model builder or context or by using any data annotations.
After upgrading to EF 6.0.2, the join table is expected to be DBFooDBBars. Why?
Note: I have "fixed" this issue by using the fluent API to bind the relationship to its proper table. What I want to know is why this table(and only this table) changed in it's convention.
Ninja Edit - rearranging the property declaration in the DBContext had absolutely zero effect.

see the answer from the EF team
https://entityframework.codeplex.com/workitem/1677
Answer by RoMiller
Hello,
Prior to EF6 there were some areas of model creation that were non-deterministic - depending on whether you were running on x86 or x64 you could get a different model. The many to many join table name was one of these areas.
In EF6 we addressed this to ensure the results would always be the same. Unfortunately, that does mean that for some models (depending on which architecture they were running on) upgrading to EF6 can cause a model change. But the model that is now calculated will be consistent across machines and future versions of EF.
If you want to continue using the original name, the best option is to use the Fluent API to specify the join table name.
~Rowan

Related

To add navigation property without foreign key in EF Core, DB-first migration with .NET Core Web API

I am working with an existing system and updating it to .NET Core, Web API and EF Core.
The existing system has 2 tables:
Parent table: Id, name, etc..
Child table: Id, ParentId, name, etc..
Though ParentId exists in the child table, there is no foreign key reference, but I want to be able to use include when I query the parent. I have asked not to add FK as part of deleting they are putting -ve values to parentId column. This way they can bring it back and a legacy system was built that way.
Now, in db-first migration how can I specify a navigation property without fk so my EF Core to act relational; or at least return them together. Adding nullable foreign key is not an option as it will break the system when -ve values are added.
I do have suggested for full cleanup of DB and getting rid of -ve values but that involves lots of testing and no deliverable. So long story short how to add navigation property without foreign key in database first migration ?
I tried adding collection and virtual entry in the model, but after migration it got overwritten. I have added this by using HasMany on modelbuilder as per this document - https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key
But scaffolding is overriding my navigation property
I found out the answer for this.
In EF core 3.x the dbcontext created by DBFrist scaffolding is all partial classes.
So I did the following:
1. new partial class for context class - here i added the relationship of navigation property using OnModelCreatingPartial() method. Example below
public partial class dbContext : DbContext
{
partial void OnModelCreatingPartial(ModelBuilder builder)
{
builder.Entity<Packcomponent>()
.HasOne(p => p.Pack)
.WithMany(b => b.PackComponent)
.HasForeignKey(p => p.PackId);
}
}
extended the partial class in a new file and added navigation property there.
public partial class Packcomponent
{
public Pack Pack { get; set; }
}
public partial class Pack
{
public List PackComponent { get; set; }
}
This way upon scaffolding it did not overwrite custom navigation properties and I also could use this properties to do EF operations like .Include() and to save related entities as well. It is pretty awesome!!

Can EF Core configure a "real" One-To-One relation where both ends are required?

The EF Core documentation about One-To-One relations says: "When configuring the relationship with the Fluent API, you use the HasOne and WithOne methods." A closer look shows that this configures One-To-ZeroOrOne or ZeroOrOne-To-ZeroOrOne relations depending on whether IsRequired is used or not. Example:
public class ParentEntity
{
public Int64 Id { get; set; }
public ChildEntity Child { get; set; }
}
public class ChildEntity
{
public Int64 Id { get; set; }
public ParentEntity Parent { get; set; }
}
The derived context class contains:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ParentEntity>().HasOne(p => p.Child).WithOne(d => d.Parent)
.HasForeignKey<ChildEntity>("ParentFk").IsRequired();
}
With this configuration, context.SaveChanges fails after context.Add(new ChildEntity()) as expected (with SqlException: Cannot insert the value NULL into column 'ParentFk' ... because of IsRequired) but succeeds after context.Add(new ParentEntity()) and context.Add(new ChildEntity() { Parent = new ParentEntity() }), i.e., the ParentEntity-ChildEntity relation is One-To-ZeroOrOne. In other words: the parent of a child is required, the child of a parent is optional.
Is there a way to configure a "real" One-To-One relation where both ends are required?
Maybe this cannot be enforced within the database. But can it be enforced by EF Core? (BTW: It can be enforced by EF6.)
Is there a way to configure a "real" One-To-One relation where both ends are required?
At the time of writing (EF Core 2.1.2), the answer is (unfortunately) negative.
The Required and Optional Relationships section of the documentation says:
You can use the Fluent API to configure whether the relationship is required or optional. Ultimately this controls whether the foreign key property is required or optional.
There is also a closed issue EF Core 2: One to One Required Not Being Enforced (also Navigation no longer needed?) #9152 asking the same question, and part of the response is:
when a relationship is made "Required" it means that a dependent entity cannot exist without an associated principal entity. This is done my making the FK non-nullable--i.e. the FK value must reference some principal entity.
However, it says nothing about the principal entity existing without the dependent. This is always possible because there isn't really any way to restrict it when working with partially loaded graphs. (This was the same with the old stack, although there were some situations where the state manager would, almost arbitrarily, stop certain things happening.) With stronger semantics applied to aggregates that limit partially loading of graphs it may be possible to enforce such a restriction in the future, but that isn't done yet.

Table per concrete type in EF Core

I have built the following model of hierarchy for DB:
public abstract class ApplicationUser : IdentityUser
{ }
public class FirstUser : ApplicationUser
{}
public class SecondUser : ApplicationUser
It is noticeable abstract Application class inherits from ASP.NET Core Identity's not abstarct class IdentityUser.
So my purpose is building different tables for UserFirst and UserSecond only, not for IdentityUser and ApplicationUser.
I tried to configure model the following:
builder.Ignore<IdentityUser>();
builder.Entity<FirstUser>().ToTable("FirstUsers");
builder.Entity<SecondUser>().ToTable("SecondUsers");
However it throws exception: Invalid column name 'Discriminator'
What can I do?
Table per Concrete Type (TPC) or Table per Type (TPT) aren't currently supported in EntityFrameework Core 1.0. Only Table per Hierarchy (TPH) is supported.
TPC and TPT are on the high priority list and may come in EntityFramewor Core 1.1 or 1.2, see the EntityFramework Core Roadmap.
Backlog Features
...
High priority features
...
Modelling
More flexible property mapping, such as constructor parameters, get/set methods, property bags, etc.
Visualizing a model to see a graphical representation of the code-based model.
Simple type conversions such as string => xml.
Spatial data types such as SQL Server's geography & geometry.
Many-to-many relationships without join entity. You can already model a many-to-many relationship with a join entity.
Alternate inheritance mapping patterns for relational databases, such as table per type (TPT) and table per concrete type TPC.
As for your question:
You can't do anything about it. If you absoloutely need this feature, you have to fall back to EntityFramework 6.x, but then you can't target .NET Core and have to target .NET Framework 4.x.
It should be noted here, that Microsoft do not feels (or recommends) to use EntityFramework Core 1.0 yet in production environment, if you require the features used from EF6. It will take several versions (at least 2 minor releases) until EntityFramework Core will get anyway close featurewise to EF6.
So if TPC is absolute requirement, go back to EF6.
Technical stuff aside, performance wise it's prefered to use TPH for mapping inheritance to your database as it avoids unnecessary joins during queries. When you use TPT/TPC every query invovling it will have to perform joins and joins are less performant.
So unless you have to map to a legacy DB designed in that way, you should fall back to TPH.
Table-per-concrete-type (TPC) mapping is now available in EFC 7.0.0 nightly builds.
https://github.com/dotnet/efcore/issues/3170
https://github.com/dotnet/efcore/issues/3170#issuecomment-1124607226
https://learn.microsoft.com/en-gb/ef/core/what-is-new/ef-core-7.0/plan#table-per-concrete-type-tpc-mapping
What you need to try it out:
.NET SDK 7.0.100-preview.4
https://dotnet.microsoft.com/en-us/download/dotnet/7.0
Visual Studio 2022 Preview 17.3
https://visualstudio.microsoft.com/vs/preview/
NuGet
Microsoft.EntityFrameworkCore 7.0.0-preview.4.22229.2
Code example:
ApplicationDbContext:
using Microsoft.EntityFrameworkCore;
namespace WebApplicationNet7.Data
{
public class ApplicationDbContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<RssBlog> RssBlogs { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().UseTpcMappingStrategy();
modelBuilder.Entity<RssBlog>().UseTpcMappingStrategy();
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
public class RssBlog : Blog
{
public string RssUrl { get; set; }
}
}
Migration will look like this:
Note that RssBlog is missing .Annotation("SqlServer:Identity", "1, 1").
You will probably get a warning that looks like this:
Microsoft.EntityFrameworkCore.Model.Validation[20609]
The property 'BlogId' on entity type 'Blog' is configured with a
database-generated default, however the entity type is mapped to the
database using table per concrete class strategy. Make sure that the
generated values are unique across all the tables, duplicated values
could result in errors or data corruption.
I could not get it to work with either setting
modelBuilder.Entity<RssBlog>().Property(u => u.BlogId).UseIdentityColumn(); or using annotation [DatabaseGenerated(DatabaseGeneratedOption.Identity)].

Reference data look-up in Entity Framework 4.2 code first

I’m putting together a code first model that has a lot of reference data. This model is based around applications, servers, and build deployments. Thus, there are a lot of many to many relationships. The pain that I’m feeling is that new records are being placed in the entity tables which I’m attempting to use as reference data. For example, we have a list of servers. I only want to see a server ONCE in the table. For all the entities referring to that server, I want them to use that row. The same can be said of my ServerRoles and Applications tables. These tables contain static data that I’m seeding and should rarely change.
I know I could solve this with look-ups and hand wiring, but I would think EF would comprehend this scenario.
Using Entity Framework code-first you can create an immutable object with protected parameter less constructor and private set properties.
It works for sure with EF 5 Beta.
[Update]
Tested also with EF 4.3.1, it works.
public class Nation
{
protected Nation() {}
public Nation(Guid id, string name)
{
Id = id;
Name = name;
}
public Guid Id { get; private set; }
public string Name { get; private set; }
}

Entity Framework CTP 5 - Code First Mappings - Can't map properly an enum list

I have the following
[DataContractAttribute]
public class Animal
{
[Key]
[XmlElement(ElementName = "Id")]
[DataMember()]
public Guid Id
{
get;
set;
}
[XmlElement(ElementName = "AnimalType")]
[DataMember()]
public List<AnimalType> AnimalType
{
get;
set;
}
}
And i map it through the code first approach with EF to tables
modelBuilder.Entity<Animal>().ToTable("Animal");
As you see I have not performed some complex mapping, but the List of AnimalType enumerations did not get mapped automatically to any columns/tables in the DB. Do i need to add some extra code to the model builder to control the mapping of an enumeration list ?
As of EF CTP5, enums are not supported yet. The team announced that they are going to fully support enums in their next RTM version which is targeted to be released on the first quarter of 2011.
I know for the longest time, enums weren't supported by EF, though I don't know if that is still the case or not.
Either way, I think there is a general problem with having EF handle a list of a type other than another entity. What is the primary key? What is the value? Should it try to store the data in one column or create a separate table and create a foreign key constraint? These are questions that will likely need to be answered before your model can be converted into a database schema.