Mapping TPT inheritance in Entity Framework 6 (core) - entity-framework-core

Consider two tables, table BaseService with PK ID, and table SubService with PK BaseServiceID, which is a foreign key to ID in the BaseService table. I wish to map these to classes in EF6 where SubService inherits from BaseService. I'm not sure how to describe in the mapping that the foreign key is from SubService.BaseServiceID to BaseService.ID. At the moment I have something like this:
modelBuilder.Entity<SubService>(e => {
e.ToTable("SubService");
});
and
modelBuilder.Entity<BaseService>(e => {
e.ToTable("BaseService");
e.HasKey(x => x.ID);
});
When I query though, the resulting query tries to join using BaseService.ID to SubService.ID. I've tried a few variations on my mapping, but I'm getting nowhere - can anyone suggest how this should be done?

From my testing, EF doesn't currently support having different column names for the keys in the tables in a TPT mapping. If you configure one entity to map its "Id" property to a column called "FooId", then all entities in the hierarchy will map their keys to "FooId".
You can create an EF Core Issue to provide feedback on this scenario.

modelBuilder.Entity<SubService>()
.ToTable("SubService")
.HasRequired(s => s.BaseService)
.WithMany(b => b.SubServices)
.HasForeignKey(s => s.BaseServiceID);
In this example, a HasRequired method is used to specify that the entity requires a and the method is used to specify the ability to navigate on the side of the relationship. Finally, `BaseService' property class'SubService' 'BaseService' 'WithMany' 'BaseService' 'HasForeign' 'KeyBase' 'ServiceID' 'SubService'

Related

EntityFramework Join table with more than 2 entities

I have 4 tables/entities which fall into two groups, alerts and recipients. Either entity from a group can map to either entity of the other group (an Alert can have many Recipients and RecipientGroups and so on).
Tables:
Alerts
AlertGroups (1 to many relationship with alerts)
Recipients
RecipientGroups (many to many relationship with recipients)
Instead of making 4 jointables (AlertRecipients, AlertRecipientGroups, etc.) I want to make one join table with 4 columns, each column being a nullable FK for one of my entity types.
I've made the table in SQL, and set up my context using Fluent API like so:
modelBuilder.Entity<AlertGroup>()
.HasMany(ag => ag.RecipientGroups)
.WithMany(rg => rg.AlertGroups)
.Map(m => m.ToTable("AlertRecipients")
.MapLeftKey("AlertGroupID")
.MapRightKey("RecipientGroupID"));
modelBuilder.Entity<AlertGroup>()
.HasMany(ag => ag.Recipients)
.WithMany(rg => rg.AlertGroups)
.Map(m => m.ToTable("AlertRecipients")
.MapLeftKey("AlertGroupID")
.MapRightKey("RecipientID"));
But I get this error:
Schema specified is not valid. Errors:
(251,6) : error 0019: The EntitySet 'AlertGroupRecipient' with schema
'dbo' and table 'AlertRecipients' was already defined. Each EntitySet
must refer to a unique schema and table.
Is there a workaround to do what I'm trying to do?
EF is not able to do that this way. With EF you need separate join table for every many-to-many relationship (because that is the way how you should do that). If you want to do that your way you cannot use many-to-many association in the mapping. You must instead "upgrade" your AlertRecipients to the real entity (another class in your model) and handle everything like one-to-many association.

Common configuration in entity framework 5

I have a couple of entities, all inherits base entity with auditing and ID fields. In the configuration for each property I have absolutely same lines like:
this.HasKey(t0 => t0.Id)
.Map(m => m.ToTable("templates"))
.Property(x => x.Id)
.HasColumnName("id")
...................
Is there way to move this code to some kind of "base configuration" to not to write it for each entity?
All you'd need to do is to implement either Table-Per-Type or Table-Per-Hierarchy:
In Table-Per-Type your entities will be split into different tables, but all offshoot tables will have its PK be a FK to the base entity table.
In Table-Per-Hierarchy your entities will all be in one table, but EF will generate a discriminator to discern which object type the entity is actually a part of.
For a clearer example of this, check out the post at this site.

Entity Framework Code first mapping without foreign key

I have two tables:
Requirement
ID (int) PK
ClientID (int)
JobNumber (int)
Comment
ID (int) PK
Job_ID (int)
Comment (varchar)
The tables don't have foreign keys and there's no possibility of adding any. I'm trying to map them in EF. I have classes for each and I'm trying to define the relationship in fluent code to map the Comment.Job_ID to the Requirement.JobNumber. A requirement can have many comments. Requirement has a list of Comments and Comment has a Requirement property.
I have this mapping setup:
modelBuilder.Entity<Comment>().HasRequired(c => c.Requirement)
.WithMany(s => s.Comments)
.HasForeignKey(f => f.Job_ID);
I'm stuck trying to get Comment.Job_ID to map to Requirement.JobNumber.
Any help appreciated.
It's not possible. With Entity Framework the entity that the Comment.Requirement navigation property is refering to is generally identified by the (primary) key property in Requirement, i.e. by ID. There is no mapping option to define that the target property is anything else than the key property - like JobNumber or another non-key property.
I could only imagine that you could "fake" the primary key property in the model to be JobNumber instead of ID (given that JobNumber is unique in the Requirement table):
modelBuilder.Entity<Requirement>().HasKey(r => r.JobNumber);
I don't know if that could have other unwished side effects. (For sure it doesn't work if JobNumber is not unique because EF wouldn't allow to have more than one entity with the same key attached to a context and updates/deletes and so on wouldn't find the correct record in the database.) It feels wrong and hacky to me. I honestly wouldn't even try that, live with the fact that you don't have a real foreign key relationship in the database, forget the navigation properties Requirement.Comments and Comment.Requirement and use manual joins in LINQ to relate the table data/entities as I need them in a given situation.

How to refer to foreign key without HasForeignKey()

Using EF 4.0 with the 4.1 upgrade for POCO / code-first.
OK so I have a domain model where type Car has, in a collection, multiple objects of type Part. So a one:many relation.
HasMany(v => v.Parts)
.WithRequired()
.HasForeignKey(v => v.CarId)
.WillCascadeOnDelete();
The problem with this is that it requires me to add a CarId property to my Part type. This is leaking ORM detail into my domain model - which is bad. Marking everything virtual is annoying enough.
Looking at the XML doc comment for the HasForeignKey() method says this:
Configures the relationship to use
foreign key property(s) that are
exposed in the object model. If the
foreign key property(s) are not
exposed in the object model then use
the Map method.
That's great and all. But it introduces a catch-22 situation because if I refactor my Part type by removing the CarId property that I don't want and update my EF model builder to not bother with mapping that property. Then as you can imagine it means I cannot then call HasKey() for defining the composite key, ala:
HasKey(v => new { v.CarId, v.PartId });
HasKey() doesn't appear to support defining the keys based upon non-Property lambdas.
What is the solution here?
If you absolutely don't like to have foreign key properties in your model you could remove the convention to detect FK properties to avoid that EF marks properties automatically as FK properties ...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions
.Remove<NavigationPropertyNameForeignKeyDiscoveryConvention>();
}
... and then simply don't specify the FK property in your mapping:
HasMany(v => v.Parts)
.WithRequired()
.WillCascadeOnDelete();
You still need CarId in your model because it is part of the primary key, but this way it doesn't act anymore as foreign key property.
Just an idea, I am not sure if it works.
Well, what about adding a new key field to CarParts table like CarPartId, so you would not need the composite key. (Composite Key support is not that great when working with ORMs.)

EF CF: many-to-many relation with additional info

We have legacy database and we map the new objects and props to the old tables and columns. So far so good. We have many-to-many relation which was mapped successfully. The intermediate table contains additional data. When we try to map the intermediate table to an object we get exception that the mapping is already defined. If we remove mapping from any side of the relation we get error that table is missing (ofc, we expect just that). I can do that easily with NHibernate and I am starting to think that EF is missing really really many features. So, please, tell me I am wrong and we can do that with EF.
Best regards
EDIT: here is a dummy sample which fails.
class User
{
public ICollection<User> Followers{get;set;}
}
class UserRelation
{
public User User{get;set;}
public User Follower{get;set;}
public DateTime CreatedOn{get;set;}
}
user mapping
modelBuilder
.Entity<User>()
.HasMany<User>(user => user.Followers)
.WithMany()
.Map(m =>m.MapLeftKey("user_id").MapRightKey("follower_id")
.ToTable("user_follower"));
user relation mapping
modelBuilder
.Entity<UserRelation>()
.ToTable("user_follower");
modelBuilder
.Entity<UserRelation>()
.HasOptional<User>(f => f.User)
.WithRequired().Map(m => m.MapKey("user_id"));
modelBuilder
.Entity<UserRelation>()
.HasOptional<User>(f => f.Follower)
.WithRequired().Map(m => m.MapKey("follower_id"));
modelBuilder
.Entity<UserRelation>()
.Property(entity => entity.CreatedOn)
.HasColumnName("created_on");
Exception
Schema specified is not valid. Errors:
(67,6) : error 0019: The EntitySet 'UserUser' with schema 'dbo' and table 'user_follower' was already defined. Each EntitySet must refer to a unique schema and table.
Edit2: Here is another example of this model: http://learnentityframework.com/LearnEntityFramework/tutorials/many-to-many-relationships-in-the-entity-data-model/
Direct many-to-many mapping is available only if junction table contains just foreign keys. If you want to expose other properties in junction table you must map it to separate entity and mapt two one-to-many relations from former entities used in many-to-many.
I'm actually not able to write you the code because I don't understand your example.
Try only (don't map Many-to-many in User):
modelBuilder.Entity<UserRelation>
.HasRequired(r => r.User)
.WithMany(u => u.Followers);
modelBuilder.Entity<UserRelation>
.HasRequired(r => r.Follower)
.WithMany();
EF maps many-to-many relationships as properties of the related objects.
So, let's say you have Cars and Drivers that are related m-to-n. In your EF model, you will see that each Car object has a Drivers collection as a property, and each Driver object has a Cars collection as a property.
That is how m-to-n relationships are modeled in EF.