Store extending classes in different tables - entity-framework-core

I have an abstract superclass which contains a [Key] field.
That class is then extended by 2 different classes which share this [Key] field, but have some fields of their own also.
But the problem is, when I create 2 DbSet<> objects for these 2 extending classes, they get put in the same table, and their fields get combined into that table.
So in my Context class, I have defined these:
DbSet<EmployeeAccount> EmployeeAccounts;
DbSet<PatientAccount> PatientAccounts;
I was expecting a separate table for each of these entities, but I got just 1 big table, and both EmployeeAccounts and PatientAccounts work with that 1 table (this leaves me with couple of columns in every row being empty).
I'm guessing this is happening because PatientAccount and EmployeeAccount have the same [Key] from their superclass.
Abstract class is really simple and looks like this:
public abstract class UserAccount : Entity<int>
{
[Key]
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
Extending classes are the following:
public class EmployeeAccount : UserAccount
{
[ForeignKey("Employee")]
public int EmployeeId { get; set; }
public Employee Employee { get; set; }
[Column(TypeName = "nvarchar(24)")]
public EmployeeType EmployeeType { get; set; }
}
and
public class PatientAccount : UserAccount
{
[ForeignKey("Patient")]
public int PatientId { get; set; }
public Patient Patient { get; set; }
public IEnumerable<FavoriteDoctor> FavouriteDoctors { get; set; }
}
Am i doing something wrong, or is this the expected behaviour?
If expected, is there a way for me to say to the EF to create 2 separate tables for these entities, even though they share the same [Key] field and superclass?
Thanks in advance.

Related

How to change a one-to-many relationship to a many-to-many relationship in Entity Framework Code First

I have two tables: Place, and MenuSection, that currently have a one-to-many relationship defined like so:
public class Place
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int PlaceID { get; set; }
public virtual ICollection<MenuSection> MenuSections { get; set; }
}
public class MenuSection
{
[Key]
public int MenuSectionID { get; set; }
public int PlaceID { get; set; }
}
However, I now need a many-to-many relationship. If I was just starting out then this would be achieved by changing the MenuSection class to look like this:
public class MenuSection
{
[Key]
public int MenuSectionID { get; set; }
public virtual ICollection<Place> Places { get; set; }
}
The problem is I already have vast amounts of data and business logic associated with the current relationship. So I figure I'll have to leave the PlaceID property in for now and add the places collection.
My question then is: how do I then tell EF the relationship is now many-to-many and to populate the auto-generated joining table with the existing relationships so that I can then remove the PlaceID property from the MenuSection class?
Alternatively I suppose I could manually create a joining table and rewrite all the business logic, manually move the existing relationships over and rewrite all the business logic like so:
public class Place
{
[Key]
[ForeignKey("Place")]
public int PlaceID { get; set; }
[Key]
[ForeignKey("MenuSection")]
public int MenuSectionID { get; set; }
public virtual Place Place { get; set; }
public virtual MenuSection MenuSection { get; set; }
}
I'm surprised this question hasn't been asked before so I just wanted to check I haven't missed a trick?

Get related data without relationship between tables

When I have two models with one to many relationship, they look like the models below:
public class Student
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int StudentID { get; set; }
public string Name { get; set; }
public int ClassID { get; set; }
[ForeignKey("ClassID")]
public virtual Class Class { get; set; }
}
public class Class
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ClassID { get; set; }
public string Name { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
This means that I can use the include method to get the related data. Now I work on a project with more than 600 tables. For these tables there are no relationships and I can not create relationships between the tables. Is there a way to link the models somehow so that I will be able to get the related data?
Yes, you can join tables without Relationships in Model Classes.
For example assume you have TeacherId in both of your tables Student and Class
You can join two tables based on TeacherId as follows
DbContext.Students
.Join(DbContext.Classes,student=>student.TeacherId,class=>class.TeacherId, (std,tchr)=> new {std,tchr});

Entity Framework foreign key object name change

I have an annoying problem that i can't seem to solve. Lets say i have a database with two tables.
Student
INT Id
NVARCHAR(30) Name
INT PrimaryTeacherId
INT SecondaryTeacherId
Teacher
INT Id
NVARCHAR(30) Name
Now when i set foreign key for PrimaryTeacherId and SecondaryTeacherId and use DatabaseFirst mapping in my project i get something like this for Student table
public partial class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int PrimaryTeacherId { get; set; }
public int SecondaryTeacherId { get; set; }
public virtual Teacher Teacher { get; set; }
public virtual Teacher Teacher1 { get; set; }
}
Note the virtual part of the class and their names, Teacher and Teacher1. No matter how i call my FKs entity framework will just override it and set increment names. That's ok if i have one or two keys to the same table but when there is more it's easy to get lost and code looks kinda annoying having object names with numbers in them. I know i can change generated classes name in my solution but when i update model changes will be lost. I'm also using Metadata partial classes for generated classes (mostly for validation and display attributes), can i change name there maybe?
TLDR: I would like to have Teacher and Teacher1 have custom names, so something like this would be awesome
public partial class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int PrimaryTeacherId { get; set; }
public int SecondaryTeacherId { get; set; }
public virtual Teacher PrimaryTeacher { get; set; }
public virtual Teacher SecondaryTeacher { get; set; }
}

EF 5.0 code first navigation property

I have got a User entity there are my users are stored in. For some users (admins) I would like to add additional details.
I have written following code.
public partial class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Display(Name = "EMail")]
[Required]
public string UserName { get; set; }
[ForeignKey("AdminDetailID")]
public virtual AdminDetail AdminDetail { get; set; }
public int? AdminDetailID { get; set; }
}
public class AdminDetail
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AdminDetailID { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile UserProfile { get; set; }
public int UserId { get; set; }
}
I like to navigate from my AdminDetail table back to my User Profile table by writing eg. admin.UserProfile.UserName. However, when I run Database-Update I receive:
The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
When I delete the UserProfile property everything works great.. How can I create a "back" navigation within my AdminDetail class?
Entity Framework Code-First allows for polymorphic classes to be stored in the same table. Have you considered using a relationship like this instead?
public partial class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Display(Name = "EMail")]
[Required]
public string UserName { get; set; }
}
public class AdminProfile : UserProfile
{
// AdminProfile members.
}
This results in a UserProfile table with an additional column called Discriminator that EF creates and manages for you. This column indicates whether each row in the table is a UserProfile or an AdminProfile. Rows which are of type UserProfile ignore the columns that are specific to AdminProfile when accessed by EF.
Entity framework handles all of the type discrimination for you so you don't need to worry about that directly. Your DbContext will simply have a DbSet which can also store entities of type AdminProfile.
You don't need to have a FK in your UserProfile class. To set up a proper 1:1 only the AdminDetail class would actually need to have the foreign key to the UserProfile class. You can still keep the virtual property to be able to navigate back and forth, and EF will know what it is that you're doing. Similar to this:
public partial class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Display(Name = "EMail")]
[Required]
public string UserName { get; set; }
public virtual AdminDetail AdminDetail { get; set; }
}

Entity Framework asp.net MVC foreign key

I am trying to code the following in code first... since I am just begining I am not able to.. please help.. thanks in advance
1. Student: Student will have student ID, First Name, Last Name
Student should belong to one class and one section(basically one to one relationship with each entity)
2. Classes: Class will have ClassId, Name
Class should have collection of students and collection of sections(basically many to many relationship with each entity)
3. Sections: Section will have SectionID, Name
Section should belong to one class and should have collection of students(basically one to one relation with class and one to many relation with Students)
Below is the code for the same
Students.cs
public class Students
{
public int StudentsId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public decimal Grade { get; set; }
public int ClassesId { get; set; }
public Classes Classes { get; set; }
public int SectionsId { get; set; }
public Sections Sections { get; set; }
}
Classes.cs
public class Classes
{
public int ClassesId { get; set; }
public string Name { get; set; }
public ICollection<Sections> Sections { get; set; }
}
Sections.cs
public class Sections
{
public int SectionsId { get; set; }
public string Name { get; set; }
public int ClassesId { get; set; }
public Classes Classes { get; set; }
public ICollection<Students> Students { get; set; }
}
If I do this I get error saying:
Introducing FOREIGN KEY constraint
'FK_dbo.Sections_dbo.Classes_ClassesId' on table 'Sections' may cause
cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON
UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I know I can get rid of this error using fluent APIs and telling not to cascade on delete, but I don't want to do that. Is there any other solution to this?? Please help
With your current model, no, there is no other way than disabling casdading delete for some of the relationships.
All your relationships are required, that means that if a class is deleted you delete the sections and the students of that class (Classes has a not exposed collection of students due to the required navigation property Classes in Students). But if the sections are deleted the students of that sections are deleted as well - and that's the second delete path to Students.
I don't know the exact meaning of your model but to me it sounds strange to delete all students of a class if the class gets deleted. Does a student always must have a class or couldn't he temporarily be without class assignment (and section assignment as well)? Maybe the student has a holiday semester for half a year and doesn't participate in any class?
In that case you could make the relationships of Students optional. Just declare the foreign key properties as nullable:
public class Students
{
//...
public int? ClassesId { get; set; }
public Classes Classes { get; set; }
public int? SectionsId { get; set; }
public Sections Sections { get; set; }
}
This would fix your problem of multiple cascading delete paths in the Students class because by default optional relationships don't have cascading delete enabled. The relationship between Classes and Sections is still required, so deleting a class will delete all sections belonging to the class as well, but it won't delete the students anymore.