Relation of entities? - c#

These are my entities:
public class Department
{
public Department()
{
Employees = new List<Employee>();
}
public int ID { get; set; }
public string Name { get; set; }
public IList<Employee> Employees { get; set; }
}
public class Employee
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int DepartmentID { get; set; }
public Department Department { get; set; }
}
public class User
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public int EmployeeID { get; set; }
public Employee Employee { get; set; }
}
Department has many employees. and every user has just one employee (one to one). how can i achive this relation with fluent code first?
thanx.

Since you are not using a shared primary key, you can map it as a "One-to-Many" relationship and ignore the "Many" side.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<User>()
.HasRequired(u => u.Employee)
.WithMany()
.HasForeignKey(u => u.EmployeeId);
}

Related

EF Core, How to update a record in table which has both One To Many and Many to Many relationships with the same entity

public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int MainCourseId { get; set; }
public Course MainCourse { get; set; }
public IList<StudentCourse> StudentCourses { get; set; }
}
public class Course
{
public int Id { get; set; }
public string CourseName { get; set; }
public string Description { get; set; }
}
public class StudentCourse
{
public int StudentId { get; set; }
public Student Student { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
}
There are two issues.
First issue is, the above migration is not creating the MainCourseId , gives this error.
"Introducing FOREIGN KEY constraint 'FK_Students_Courses_MainCourseId' on table 'Students' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors."
Second is, in another system where structure of entities are same, updating an existing record with a modifed value in MainCourseId doesn't work.
Your db structure has a bug. You need to add List of StudentCourse to Course class too. IMHO it is not the best db structure I have seen. Much better way is to add Id to StudentCourse table and a flag if it is a main course.
public class StudentCourse
{
public int Id { get; set; }
public int StudentId { get; set; }
public Student Student { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
public bool IsMainCourse {get; set;}
}
and since you are using net5 you can add another lists to the classes
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Course> Courses { get; set; }
public virtual List<StudentCourse> StudentCourses { get; set; }
}
public class Course
{
public int Id { get; set; }
public string CourseName { get; set; }
public string Description { get; set; }
public virtual List<Student> Students{ get; set; }
public virtual List<StudentCourse> StudentCourses { get; set; }
}
you need to make a new init migration to use this structure
You can't have cascade delete that creates a multiple cascade paths. Here deleting a Course would delete both all the StudentCourses for that Course and all the Students that have that as their MainCourse.
But EF won't configure CascadeDelete if your MainCourse is optional, as it probably should be to enable you to store a Student that has no MainCourse. So to fix both issues, just make Student.MainCourseId optional by changing the type to int?.:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int? MainCourseId { get; set; }
public Course MainCourse { get; set; }
public IList<StudentCourse> StudentCourses { get; set; }
}
Or you can leave it as required and configure the relationship not to cascade deletes.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasOne(s => s.MainCourse)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<StudentCourse>()
.HasKey(e => new { e.StudentId, e.CourseId });
base.OnModelCreating(modelBuilder);
}
I think it might be more convenient to use "skip navigation properties" so Student has Courses, and a Course has Students, which you can do in EF Core 5+ with Many-to-Many mapping, like this:
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System;
using System.Collections.Generic;
namespace EfCore6Test
{
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int? MainCourseId { get; set; }
public Course MainCourse { get; set; }
public ICollection<Course> Courses { get; } = new HashSet<Course>();
}
public class Course
{
public int Id { get; set; }
public string CourseName { get; set; }
public string Description { get; set; }
public ICollection<Student> Students { get; } = new HashSet<Student>();
}
public class StudentCourse
{
public int StudentId { get; set; }
public Student Student { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
}
public class Db: DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses{ get; set; }
public DbSet<StudentCourse> StudentCourses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasOne(s => s.MainCourse).WithMany().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Student>().HasMany(s => s.Courses).WithMany(c => c.Students).UsingEntity<StudentCourse>(
sc => sc.HasOne(e => e.Course).WithMany(),
sc => sc.HasOne(e => e.Student).WithMany()
);
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=localhost;database=efCore6Test;Integrated Security=true;TrustServerCertificate=true", o => o.UseRelationalNulls(true))
.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information);
base.OnConfiguring(optionsBuilder);
}
}
class Program
{
static void Main(string[] args)
{
{
using var db = new Db();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var joe = new Student() {Name="Joe" };
var math = new Course() { CourseName = "Math" };
var english = new Course() { CourseName = "English" };
joe.Courses.Add(math);
joe.Courses.Add(english);
joe.MainCourse = english;
db.Students.Add(joe);
db.SaveChanges();
}
{
using var db = new Db();
var joe = db.Students.Where(s => s.Name == "Joe").First();
var math = db.Courses.Where(c => c.CourseName == "Math").First();
joe.MainCourse = math;
db.SaveChanges();
}
}
}
}

Define relationship in EF Core

How do I define the relationships here with EF Core?
I have an Employee table which has multiple Jobs
public class Employee
{
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<HourlyRate> DefaultRate { get; set; }
public string Note { get; set; }
public bool Active { get; set; }
public DateTime DateHired { get; set; }
public List<PhoneNumber> PhoneNumbers { get; set; }
public List<Address> Addresses { get; set; }
public List<Job> Jobs { get; set; }
public bool Deleted { get; set; }
}
And the Job class has an Employee object to navigate back to the employee and the Job has multiple Directors which are also Employees
public class Job
{
public int JobId { get; set; }
public Employee Employee { get; set; }
public JobType Type { get; set; }
public Department Department { get; set; }
public List<Employee> Directors { get; set; }
public bool Active { get; set; }
public decimal HourlyRate { get; set; }
public string Note { get; set; }
public bool Deduction { get; set; }
public int? DeductionPercent { get; set; }
}
This is my DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasMany(employee => employee.Jobs)
.WithOne(i => i.Employee);
}
Initially the Job only had a single Director and everything was good but the requirement has changed to have multiple directors and ef removed the Director column from the Job table and added a JobId column to the Employee table but the problem is that if i add that director to a second job by job.Directors.Add(director) EF overrides the job id of the of the director and the director is being removed from the previous job
I am using EF Core 2.2
if a Job has only 1 Employee but multiple Directors (also Employee)
add public int EmployeeId {get; set;} to your Job class and add this
modelBuilder
.Entity<Job>()
.HasMany(p => p.Directors)
.WithMany(p => p.Jobs));
also, change List<> to ICollection<>
You should tell EF through fluent API that there's a 1-to-many relationship from Employee to Job. Otherwise, EF may get confused.
The many-to-many relationship needs a junction table and matching entity in the model which you'll also need to configure through fluent API. You'll define two 1-to-many relationships from Employee and Job to that new entity. EF core does not directly support such relationships before 5.0.
If you are targeting SQL, then you need to mark at least one of the relationships as OnDelete(CascadeBehavior.NoAction). Otherwise, your model will generate invalid table defintions which will raise errors at creation time.
Update:
The junction table would be defined something like this.
public class Employee
{
// ... other stuff
public List<EmployeeJob> EmployeeJobs { get; set; }
}
public class Job
{
// ... other stuff
public List<EmployeeJob> EmployeeJobs { get; set; }
}
public class EmployeeJob
{
public int EmployeeId { get; set; }
public int JobId { get; set; }
public Employee Employee { get; set; }
public Job Job { get; set; }
}
// goes in DbContext
modelBuilder.Entity<EmployeeJob>.HasKey(x => new { x.EmployeeId, x.JobId });
Try to use this code. Since your employee can have one or many jobs I added the table EmployeeJob and many-to-many relations. I think you just need to add IsDirector flag to Employee or maybe better something like an EmployeeType:
public class Employee
{
public Employee()
{
EmployeeJobs = new HashSet<EmployeeJob>();
}
[Key]
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Note { get; set; }
public bool Active { get; set; }
public DateTime DateHired { get; set; }
public bool Deleted { get; set; }
[InverseProperty(nameof(EmployeeJob.Employee))]
public virtual ICollection<EmployeeJob> EmployeeJobs { get; set; }
}
public class Job
{
public Job()
{
EmployeeJobs = new HashSet<EmployeeJob>();
}
[Required]
public int JobId { get; set; }
public bool Active { get; set; }
public decimal HourlyRate { get; set; }
public string Note { get; set; }
public bool Deduction { get; set; }
public int? DeductionPercent { get; set; }
[InverseProperty(nameof(EmployeeJob.Job))]
public virtual ICollection<EmployeeJob> EmployeeJobs { get; set; }
}
public class EmployeeJob
{
[Key]
public int Id { get; set; }
public int EmployeeId { get; set; }
[ForeignKey(nameof(EmployeeId))]
[InverseProperty(nameof(EmployeeJob.Employee.EmployeeJobs))]
public virtual Employee Employee { get; set; }
public int JobId { get; set; }
[ForeignKey(nameof(JobId))]
[InverseProperty(nameof(EmployeeJob.Employee.EmployeeJobs))]
public virtual Job Job { get; set; }
}
public class EmployeeDbContext : DbContext
{
public EmployeeDbContext()
{
}
public EmployeeDbContext(DbContextOptions<EmployeeDbContext> options)
: base(options)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<EmployeeJob> EmployeeJobs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=localhost;Database=Employee;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<EmployeeJob>(entity =>
{
entity.HasOne(d => d.Employee)
.WithMany(p => p.EmployeeJobs)
.HasForeignKey(d => d.EmployeeId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_EmployeeJob_Employee");
entity.HasOne(d => d.Job)
.WithMany(p => p.EmployeeJobs)
.HasForeignKey(d => d.JobId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_EmployeeJob_Job");
});
}

ASP.NET MVC customer portal User Junction Table

Im building a management portal for a chain of restaurants. I am using ASP.NET MVC with EF Code First.
I want each user to, after login, only see the rescources that are connected to them. I want to put a junction table(many-to-many) between ApplicationUser and the Restaurant-class(model), since each user can have/work at many restaurants, and each restaurant can have many owners/workers.
How do you do this in EF Code first? The same way I did Restaurant --> Menue? Do you need to build a new DBContext for Applicationuser for this to work?
public class Restaurant
{
public int Id { get; set; }
public string Name { get; set; }
public string Adress { get; set; }
public string PhoneNumber { get; set; }
public DateTime StartDate { get; set; }
//Connections
public virtual ICollection<Menue> Menues { get; set; }
}
public class Menue
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public DateTime ModifyDate { get; set; }
//FK For RestaurantConnection
public int RestaurantId { get; set; }
}
For many to many configuration do like this
Student class should have a collection navigation property for Course, and Course should have a collection navigation property for student
public class Student
{
public Student()
{
this.Courses = new HashSet<Course>();
}
public int StudentId { get; set; }
[Required]
public string StudentName { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public Course()
{
this.Students = new HashSet<Student>();
}
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
In your DbContext add this configuration
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasMany<Course>(s => s.Courses)
.WithMany(c => c.Students)
.Map(cs =>
{
cs.MapLeftKey("StudentRefId");
cs.MapRightKey("CourseRefId");
cs.ToTable("StudentCourse");
});
}
For more information read this article Configure Many-to-Many relationship

navigation property is not loaded

I am using a objectDataSource to populate a gridview. I have 2 simple classes :
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public int Salary { get; set; }
public Department Department { get; set; }
}
and
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public List<Employee> Employees { get; set; }
}
I also have
public class EmployeeDBContext : DbContext
{
public DbSet<Department> Departments { get; set; }
public DbSet<Employee> Employees { get; set; }
}
now in my EmployeeRepository class i have
public List<Department> GetDepartments()
{
EmployeeDBContext employeeDBContext = new EmployeeDBContext();
return employeeDBContext.Departments.Include("Employees").ToList();
}
Even though I have added .Include("Employees"), the employees are missing in gridview. what am I doing wrong here?
First of all, you will need to have a foreign key (DepartmentId) inside Employee class. I do not know how the video got away with that.
public class Employee
{
public int Id { get; set; }
public int DepartmentId { get; set; } <=====
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public int Salary { get; set; }
public virtual Department Department { get; set; }
^^^^^^^
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
^^^^^^^^^^^^^^^^^^
}
public partial class EmployeeDBContext : DbContext
{
public virtual DbSet<Employee> Employee { get; set; }
public virtual DbSet<Department> Department { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Optional, but good practice to have the mapping here.
modelBuilder.Entity<Department>()
.HasMany(e => e.Employee)
.WithRequired(e => e.Department)
.HasForeignKey(e => e.DepartmentId);
}
}
- OR -
Add DepartmentId property and [ForeignKey] data annotation to Department.
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public int Salary { get; set; }
public int DepartmentId { get; set; } <===
// Optional, but good practice to have this data annotation attribute.
[ForeignKey("DepartmentId")] <===
public Department Department { get; set; }
}
FYI: You want to use virtual, in case someone wants to use lazy loading in the future.
have you tried something like this
return employeeDBContext.Departments.Include(x =>x.Employees ).ToList(); ?

Can I create multiple associations between two entities in Entity Framework 5, c#?

E.g. I have 2 entities:
public class Department
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
public Employee Manager { get; set; }
}
public class Employee
{
public Guid Id { get; set; }
public string FullName { get; set; }
[ForeignKey("DepartmentId")]
public Department Department { get; set; }
public Guid DepartmentId { get; set; }
public string Position { get; set; }
}
I know I can associate (as I already did:) Department.Employees with Employee.Id (and inverse: Employee.Department with Department.Id).
Questions:
1) Is this one or two associations?
2) Can I create the second association between Department.Manager and Employee.Id? Don' want to store Managers in another table so they are also stored in Employee table and have in Position field "Manager".
Define the relationship as follows.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>()
.HasRequired(a => a.Manager)
.WithMany()
.HasForeignKey(a => a.EmployeeId);
}
You want them virtual too if you want lazy loading and change tracking.
public class Department
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
public virtual Employee Manager { get; set; }
}

Categories

Resources