EntityFramework Join table with more than 2 entities - entity-framework

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.

Related

Mapping TPT inheritance in Entity Framework 6 (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'

Adding DateTime field to many-to-many EF Code First relationship

I am using EF 6 Code-First, table per type, and I have two concrete classes Group and User. Group has a navigation property Members which contains a collection of User. I have mapped this many-to-many relationship in EF using Fluent syntax:
modelBuilder.Entity<Group>
.HasMany<User>(g => g.Members)
.WithMany(u => u.Groups);
I would like to be able to say when a member has joined a group so that I can query for, say, the newest member(s). I am not sure of how this is best accomplished within the framework.
I see the following options:
Create and use an audit table (ie GroupMembershipAudit consisting of Group, User, join/unjoin, and DateTime
Add a column to the autogenerated many-to-many table between User and Group
Is there anything within EF to facilitate this sort of storage of many-to-many historical info like this / append columns to the many-to-many relationship?
Add a column to the autogenerated many-to-many table between User and
Group
That is not possible - auto-generated junction tables can contain only keys (that is called Pure Join Table). According to Working with Many-to-Many Data Relationships article: If the join table contains fields that are not keys, the table is not a PJT and therefore Entity Framework cannot create a direct-navigation (many-to-many) association between the tables. (Join tables with non-key fields are also known as join tables with payload.)
Create and use an audit table (ie GroupMembershipAudit consisting of
Group, User, join/unjoin, and DateTime
Actually you should create GroupMembershipAudit entity. With Code First table will be generated, you don't need to create it manually.

How can I correctly design relation of few different entities (one-many) that relate to one common?

I have few entities that related to each other (just a sample):
------User----------
->(One) Type
->(One) Company
- Name
------Type----------
->(Many) User
- Name
------Job------------
->(Many) User
->(Many) Type
- Name
------Company-----
->(Many) Users
->(Many) Job
- Name
(Name it's just a text.)
And I want to have many Names (aliases) for each row of entities and store it in one other table.
I have create new entity Name and configure EF next:
modelBuilder.Entity<User>().HasMany(e => et.Names);
modelBuilder.Entity<Type>().HasMany(e => et.Names);
modelBuilder.Entity<Job>().HasMany(e => et.Names);
modelBuilder.Entity<Company>().HasMany(e => et.Names);
It creates one table that looks loke this:
[Id],
[Name],
[User_Id],
[Type_Id],
[Job_Id],
[Company_Id]
(Can I awoid many _Ids any how?)
And when I try to set cascade deleting it throw me exception ".may cause cycles or multiple cascade paths".
modelBuilder.Entity<User>()
.HasMany(pt => pt.Names)
.WithOptional()
.WillCascadeOnDelete(true);
What is the best way to do this using entity framework?
A better solution may be to have a distinct "Name" Entity for each entity that requires many Names. It would result in a true one-to-many mapping at the database level, but require a separate table for each. It should also prevent the cascading issue.

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.

The Many To Many relation will not be mapped when using ADO.net entity framework

I have these three tables inside my sql server but when mapping these tables using EF I will lose the table representing the M-M relation -the accountsitemapping table- and in this case I will not be able to know which accounts are linked to which sites.
To workaround this problem I just added a new column to the AccountSiteMapping table and I re-mapped the tables again then I can access the AccountSiteMapping table.
But is there a way to be able to solve this problem without the need to modify my table design ?
By default entity framework will hide all mapping tables in many-to-many relationships. You will have to do your queries this way:
For example, finding SiteDefinitions associated with a given org_ID:
db.SiteDefinitions.Where(a => a.AccountDefinitions.Any(b => b.ORG_ID == org_ID));