Entity Framework one-to-many relation - c#

I have two simple classes:
class Student
{
public int StudentId { get; set; }
public int IndeksNo { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public virtual Group Group { get; set; }
}
And
class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public virtual List <Student > Students { get; set; }
}
Database is created correctly (using Code First). I have added couple items to both tables, but all the time Students list and Group property in Student are null. I have no idea why. I have searched solution for about an hour and i came up with something like this:
modelBuilder.Entity<Student>()
.HasRequired(st => st.Group)
.WithMany(gr => gr.Students)
.WillCascadeOnDelete(false);
But it doesn't help. I have no idea what may went wrong or why Group.Students and Student.Group are always null. List of groups and list of students are selected from db successfully - i mean all params except those connections.

In order to use the Lazy Loading feature of EntityFramework. Your navigation property must be virtual. In your Student class in the the case. The problem is with your Group class. The navigational property is a virtual List<Student> it must be a virtual ICollection<Student>
You can simply change your Group to
class Group
{
public int GroupId { get; set; }
public string Name { get; set; }
public virtual ICollection<Student > Students { get; set; }
}

Related

EF model change one-to-one relationship to many-to-many relationship

I have two tables with one-to-one relationship.
public class Name
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Category { get; set; }
public int? NameId { get; set; }
[ForeignKey("NameId ")]
public virtual Name Name { get; set; }
}
I already have data in those tables.
I know the database relations are not supported to be changed.
Is it possible to change one-to-one relationships to many-to-many relationships?
What is the most suitable approach to overcome this requirement?
Yes, you can still change that, using migrations.
Step 1 is to create a linking table, like NameCategories, which looks something like this:
public class NameCategories
{
public int Id { get; set; }
public int NameId { get; set; }
public Name Name { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
Step 2 is to reference this table in the tables you already have. In Name it would look like this
public class Name
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<NameCategory> Categories { get; set; }
}
Step 3 is to add a migration. You'll have some AddColumn() and some DropColumn() statements. In between them, when all the add stuff was executed but the drops not yet, you can add SQL code to carry over all the existing relations into the newly created table. After that, the old data will be deleted by the DropColumn() code. In your example, this would look something like this
INSERT INTO NameCategories (NameId, CategoryId)
SELECT (n.Id, c.Id) FROM Names n
JOIN Categories c on c.NameId = n.Id
WHERE ..
You can execute the SQL in the migration like this:
var sql = #"...";
Sql(sql);
I hope this helps you out!

How do I create a review entity as a child of a parent entity in Entity Framework [duplicate]

I have two entities in my MVC application and I populated the database with Entity Framework 6 Code First approach. There are two city id in the Student entity; one of them for BirthCity, the other for WorkingCity. When I define the foreign keys as above an extra column is created named City_ID in the Student table after migration. Id there a mistake or how to define these FKs? Thanks in advance.
Student:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
public virtual City LivingCity { get; set; }
}
City:
public class City
{
public int ID { get; set; }
public string CityName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
To achieve what you want you need to provide some aditional configuration.Code First convention can identify bidirectional relationships, but not when there are
multiple bidirectional relationships between two entities.You can add configuration (using Data Annotations or the Fluent API) to present this
information to the model builder. With Data Annotations, you’ll use an annotation
called InverseProperty. With the Fluent API, you’ll use a combination of the Has/With methods to specify the correct ends of these relationships.
Using Data Annotations could be like this:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
[InverseProperty("Students")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
public virtual City LivingCity { get; set; }
}
This way you specifying explicitly that you want to relate the BirthCity navigation property with Students navigation property in the other end of the relationship.
Using Fluent Api could be like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity)
.WithMany(m => m.Students).HasForeignKey(m=>m.BirthCityId);
modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity)
.WithMany().HasForeignKey(m=>m.LivingCityId);
}
With this last solution you don't need to use any attibute.
Now, the suggestion of #ChristPratt in have a collection of Student in your City class for each relationship is really useful. If you do that, then the configurations using Data Annotations could be this way:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
[InverseProperty("BirthCityStudents")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
[InverseProperty("LivingCityStudents")]
public virtual City LivingCity { get; set; }
}
Or using Fluent Api following the same idea:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity)
.WithMany(m => m.BirthCityStudents).HasForeignKey(m=>m.BirthCityId);
modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity)
.WithMany(m => m.LivingCityStudents).HasForeignKey(m=>m.LivingCityId);
}
Sheesh. It's been a long day. There's actually a very big, glaring problem with your code, actually, that I completely missed when I commented.
The problem is that you're using a single collection of students on City. What's actually happening here is that EF can't decide which foreign key it should actually map that collection to, so it creates another foreign key specifically to track that relationship. Then, in effect you have no navigation properties for the collections of students derived from BirthCity and LivingCity.
For this, you have to drop down to fluent configuration, as there's no way to configure this properly using just data annotations. You'll also need an additional collection of students so you can track both relationships:
public class City
{
...
public virtual ICollection<Student> BirthCityStudents { get; set; }
public virtual ICollection<Student> LivingCityStudents { get; set; }
}
Then, for Student:
public class Student
{
...
public class StudentMapping : EntityTypeConfiguration<Student>
{
public StudentMapping()
{
HasRequired(m => m.BirthCity).WithMany(m => m.BirthCityStudents);
HasRequired(m => m.LivingCity).WithMany(m => m.LivingCityStudents);
}
}
}
And finally in your context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new Student.StudentMapping());
}

InverseProperty Entity Framework 7

I am facing a problem with EF7 inverse property. There are two entities that are connected like this.
public class Employee
{
public int Id { get; set; }
}
public class Review
{
public int Id { get; set; }
[Required]
public virtual Employee Employee { get; set; }
[Required]
public Employee Manager { get; set; }
}
I want to access a list of the reviews when I start to query my employees, so I tried to do this:
public class Employee
{
public Employee()
{
Reviews = new List<Review>();
}
public int Id { get; set; }
[InverseProperty("Employee")]
public virtual ICollection<Review> Reviews { get; set; }
}
With this, the query is not well made and return this error:
Invalid column name 'EmployeeId1'.
This is the part of the query where is the error:
SELECT [ua].[Id], [r].[EmployeeId], [r].[EmployeeId1], [r1].[EmployeeId], [r1].[EmployeeId1]
FROM [UserAssessment] AS [ua]
LEFT JOIN [Review] AS [r] ON [ua].[ReviewId] = [r].[Id]
LEFT JOIN [Review] AS [r1] ON [ua].[ReviewId] = [r1].[Id]
Anyone know what can I do?
UPDATE
This statement is generating the query:
return this.DbSet
.Include(ua => ua.Employee).ThenInclude(t => t.Role)
.Include(ua => ua.Review).ThenInclude(rt => rt.ReviewType)
.Include(ua => ua.Review).ThenInclude(rt => rt.Manager).ThenInclude(r => r.Role)
I have to access with those same includes because lazy loading is not available on EF7 yet.
You need the InverseProperty on both the Employee and Review
public class Review
{
public int Id { get; set; }
[Required]
[InverseProperty("Reviews")]
public Employee Employee { get; set; }
[Required]
public Employee Manager { get; set; }
}
public class Employee
{
public int Id { get; set; }
[InverseProperty("Employee")]
public ICollection<Review> Reviews { get; set; }
}
Should work. I have a similar setup where it creates the navigation without creating any new fields. If this doesn't work let me know and I'll spin up a test project.
Also note, that EF7 currently ignores virtual and this does not have meaning like it did in EF6.

updating data in many-to-many relationship in entity framework in code first existing database

I am working on app and struggling to really understand how to update data in tables where we have intermediate join table (i.e. table to break many-to-many relationship). for example, from following diagram. if I say I want to add record for new student with list of three courses i.e. math, English and computing. how I do that where I have
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
another questions; another scenario if I have courses already and of-course I don't want duplication of math, English and computing course title, how I add new instance of student record there??
Student
public partial class Student
{
public Student()
{
this.StudentCourses = new HashSet<StudentCourse>();
}
public int StudentID { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
}
Course
public partial class Course
{
public Course()
{
this.StudentCourses = new HashSet<StudentCourse>();
}
public int CourseID { get; set; }
public string Title { get; set; }
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
}
Intermediate model
public partial class StudentCourse
{
public int StudentCourseID { get; set; }
public int StudentID { get; set; }
public int CourseID { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
My third question is, do I have to use Virtual key word in above code.
First, if you do not have an additional field in StudentCourse (such semester of the registration), then you do not need to have StudentCourse class.
If you want to keep this mode, you can do this:
StudentCourse registration = new StudentCourse();
registration.StudentID = 4;
registration.CourseID = 6;
context.StudentCourses.Add(registration);
context.SaveChanges();
These resources may give you further explanation:
https://practiceaspnet.wordpress.com/2015/10/22/code-first-many-to-many-mm-relationships-using-conventions-and-data-annotations/
https://practiceaspnet.wordpress.com/2015/10/30/managing-data-in-many-to-many-relationships-with-entity-framework-code-first/
If you want to check if there is no duplicate, you can simple do these:
if(context.Courses.Where(c => c.Title == 'Math').FirstOrDefault() == null)
{
//add the course
}else {
//already existing
}

Why do I need to have a virtual property with EF5 in my child class?

I am using Entity Framework 5 with Code First and lazy loading disabled. Given the following:
public class Department
{
public int DepartmentID { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
}
// Mapping Relationships for Course
<code here>
this.HasRequired(t => t.Department)
.WithMany(t => t.Courses)
.HasForeignKey(d => d.DepartmentID);
Can someone explain why I would need to have the:
public virtual Department Department { get; set; }
What would happen if I completely remove this line? What would I not be able to do? As far as I can see the only thing I could not do is to use Entity Framework to check my foreign key. However I already have this set up in the database so I would still get an error from the Db.
The Department property exists as a link to a specific department for a course.
It can be part of a one to many relationship which gives you access to a department from a course.
It contains your Department information, Depertment Name Courses information.I think you can remove it because the information of Department you can also access by searching for its DepartmentID. But it would be better to keep it.

Categories