EF Core 6.0 Multiple relationships between two tables - sql-server

Let's say I have two tables - User and TrainingGroup.
Relation between these two tables has to be like this:
User can be in only one TrainingGroup
One TrainingGroup can have multiple users
The problem happens because I need the following relationships as well:
User can coach many TrainingGroups (group coach, eg. kids group, adults group etc.)
Group can have only one user (coach)
Also, it is not possible for a user to be a coach and member of the group at the same time.
Is it possible to achieve that, and how? Any other design ideas?
Code block below roughly explains what I'm trying to achieve:
public class TrainingGroup
{
public User Coach { get; set; } // Coach of the group
public int CoachId { get; set; }
public List<User> Members { get; set; } //Members in the training group
}
public class User
{
public int TrainingGroupId { get; set; }
public TrainingGroup TrainingGroup { get; set; } // Group the user (Member) is training in
public List<TrainingGroup> CoachingGroups { get; set; } // Groups that the user is coaching
}

Related

EF Improve performance of Many-to-Many / .Include .ThenInclude

I've got a relatively basic model - Users and Tags. There is a fixed list of Tags. A User can have multiple Tags and a Tag can be used by multiple users.
I had gone with structure below and finding performance issues when returning results.
public class User
{
public string Id {get; set;}
public virtual List<UserTag> UserTags {get; set}
}
public class UserTag
{
public string UserId { get; set; }
public User User { get; set; }
public int TagId { get; set; }
public Tag Tag{ get; set; }
}
public class Tag
{
[Key]
public int TagId { get; set; }
public string Name { get; set; }
public virtual List<UserTag> UserTags { get; set; }
}
I have the following query which is takings a long time (several seconds):
var x = db.Users.Include(u => u.UserTags).ThenInclude(u => u.Trait).ToList<User>();
I have tried writing it as such, which has improved the time, however it is still taking too long:
db.UserTags.Load();
db.Tags.Load();
var x = db.Users.ToList<User>();
Is there any other way to speed this up? Running a query directly in SQL SMS is almost instant (e.g.
select * from Users u left outer join UserTags t on t.UserId = u.Id)
In terms of data rows, it is apx Tags: 100, UserTags:50,000, Users: 5,000
First you can check how EF translates your request to SQL Server - therefore use the "SQL Server Profiler"
Then you could use the genereated query to check if there might be an missing index which speeds up the query
You also can try to write a Join instead of ThenInclude and see how the query then behaves
best regards
Jimmy

Auto assign column value when row is created

I am using EF6 and SQL Server 2014.
I have something like the following simplified model:
public class Order
{
public int Id { get; set; }
public DateTime Created { get; set; }
public IEnumerable<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
public string ItemName { get; set; }
public int OrderItemNumber { get; set; }
}
The Id properties are standard auto-increment primary keys.
The OrderItem.OrderItemNumber property uniquely identifies an OrderItem within the context of a given Order.
When a new OrderItem row is inserted it needs to be assigned the next OrderItemNumber for the parent Order, something like
SELECT COALESCE(MAX(OrderItemNumber),0) FROM dbo.OrderItems WHERE OrderId = 2
Is there a means using EF6/SQL Server to auto-assign the OrderItemNumber at the point a row is inserted?
Although this proved an interesting learning experience on EF support for triggers (see https://github.com/NickStrupat/EntityFramework.Triggers) I eventually decided to re-model.
In this case my question was a result of incorrect modelling. The OrderItemNumber property was redundant so I removed it; the sequence the OrderItems were created can be inferred from the Id value, and there was no real requirement to allow re-sequencing.

Including the Foreign Key Id in Code First to save queries?

I'm using Entity Framework 6 Code First.
Right now my model looks the following:
public class Region
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<City> Cities { get; set; }
}
public class City
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Region Region { get; set; }
}
The thing is, I got a list of cities.
I need to lookup the region of each city (I have a local list of regions as well).
Now, I suppose I could do the following:
foreach (var c in cities)
{
if (regions.Any(x => x.Id == c.Region.Id))
}
Here I will have to look up a region from the database for each city (lazy loading).
However, I only need the Id of the region, therefore it seems wasteful to me to look up the region row at every loop.
If I changed my City model to be the following:
public class City
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("Region")]
public int RegionId { get; set; }
[Required]
public virtual Region Region { get; set; }
}
I could do the following instead:
foreach (var c in cities)
{
if (regions.Any(x => x.Id == c.RegionId)) //no region lookup at Im using the foreign id key
}
Is this correct? I mean it will save me a query for every city right?
If so, is there any reason NOT to include the foreign id keys in the model when doing Code First at all?
Is this correct? I mean it will save me a query for every city right?
It will save a join to the Regions table. You have to check it out with the SQL Profiler! For example:
List of cities with Id City1, City2 (Region not loaded City1= has RegionId 5)
Now you are looking for any City has a Region with Id 5.
// EF does not have to join the tables because you have the RegionId
if (myDbContext.Cities.Any(c => c.RegionId == 5))
{
}
If so, is there any reason NOT to include the foreign id keys in the
model when doing Code First at all?
No for me this it is a good practice! Just keep it consistent and do it for all relationships of the type 1..n/1..0or1.

Entity Framework Code First - One to Many relations from multiple tables

I have a BlobEntity table that contains paths to files for many other tables (tableX, tableY, tableZ, etc...) in my application.
The relation between all the other tables to BlobEntity table is one to many.
Example:
tableX -> BlobTable (OTM)
tableY -> BlobTable (OTM)
tableZ -> BlobTable (OTM)
and the relation is:
public virtual ICollection<BlobEntity> BlobEntity { get; set; }
I'm not sure if this is an issue, but entity framework Code First creates a new FK column in BlobEntity table for each source table.
In my case, BlobEntity contains three FK columns for tableX, tableY and tableZ.
In order to be efficiency, i rather create one column in BlobEntity that contains the FK for the source tables.
Is it reasonable?
Please advise...
Thanks.
No, you can't do this even in plain old SQL.
You can have a foreing key pointing to more than one table; that's why you need
three columns.
If you want to do a "trick" like this, you have to manually manage the relation (I mean, no real FK), but you can't map it into EF.
What about this?
public class EntityA
{
public int Id { get; set; }
public int MyFileID {get;set;}
public virtual MyFiles MyFile { get; set; }
}
public class EntityB
{
public int Id { get; set; }
public int MyFileID {get;set;}
public virtual MyFiles MyFile { get; set; }
}
public class MyFiles
{
public MyFiles()
{
// ReSharper disable once VirtualMemberCallInContructor
FilesForEntityA = new List<EntityA>();
// ReSharper disable once VirtualMemberCallInContructor
FilesForEntityB = new List<EntityB>();
}
public int Id { get; set; }
public int? EntityAId {get;set;}
public int? EntityBId {get;set;}
public virtual ICollection<EntityA> FilesForEntityA { get; set; }
public virtual ICollection<EntityB> FilesForEntityB { get; set; }
}
This way you can have the FK in place and you can easily manager multiple entities.
Obviously if you have many files for each entity, you can go with a N-to-N relationship, like this.

Is it possible to have a relation where the foreign key is also the primary key?

I have a table in a SQL database which should have a relation with one of two tables, but not both.
My manager solved it by using the ID field of the table Competitor as both primary key and as foreign key for the two other tables. None of the tables have autoincremental IDs.
Problem is, when I try to add a new compeitor, it fails unless there is an entry in both of the other tables with the specified ID, which is the opposite of what I want.
Here is an illustration to show you how it's done:
I am sorry if this has been posted or answered before. I could not find anything while searching.
Best Regards
Kajac
The only way to get this right is by subtyping the two Equestrian classes:
public class EquestrianBase
{
public int Id { get; set; }
public virtual Competitor Competitor { get; set; }
}
public class Equestrian : EquestrianBase
{
// Other properties
}
public class TeamEquestrian : EquestrianBase
{
public int TeamLeaderPersonId { get; set; }
// Other properties
}
This could be the competitor class:
public class Competitor
{
public int Id { get; set; }
public virtual EquestrianBase EquestrianBase { get; set; }
// Other properties
}
And the essential configuration:
modelBuilder.Entity<EquestrianBase>()
.HasRequired(e => e.Competitor)
.WithOptional();
Using this model, you'll see that EF adds a Discriminator column to the Equestrian table (of which there is only one now) which discriminates between the two types. Now the database schema enforces that a Competitor have only one Equestrian of either type.
If you want to further fine-tune the inheritance schema, which is called table per hierarchy, look here.
Sure just set the key as Foreign and Primary on the "dependant properties". The Competitor should have the primary key.
public class Competitor
{
[Key]
public int Id { get; set; }
}
public class Equestrain
{
[Key]
[ForeignKey("Competitor")]
public int Id{ get; set; }
public Competitor Competitor { get; set; }
}
public class TeamEquestrain
{
[Key]
[ForeignKey("Competitor")]
public int Id{ get; set; }
public Competitor Competitor { get; set; }
}
MSDN - Configuring a Required to Optional Relationship (One to Zero or One)

Resources