Multiple Foreign Keys to one entity - c#

I am developing a system that volunteers can apply to be a part of ceremonies/appointments for a church.
I want to have it so multiple volunteers can be part of a ceremony, might be 1 or could be 4. I've seen various examples on SO that use just two foreign keys but I just can't wrap my head around more than two. I know I need inverse property but I'm just confused.
Here's the two entities I need. I think I need to have Appointment/Ceremony in the volunteer as an ICollection. I may be way off, I'm not too sure.
public class Volunteer
{
[Key]
public int VolunteerId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public bool GardaVetted { get; set; }
public string VolunteerRole { get; set; }
public string VolunteerPhoneNumber { get; set; }
[ForeignKey("Church")]
public int ChurchId { get; set; }
[ForeignKey("Appointments")]
public int AppointmentId { get; set; }
//Foreign Key
public virtual Church Church { get; set; }
public virtual ICollection<Appointments> Appointments { get; set; }
}
public class Appointments
{
[Key]
public int AppointmentId { get; set; }
public string DetailsOfAppointment { get; set; }
public int? Fee { get; set; }
public string RoomType { get; set; }
public string NameOfApplicant { get; set; }
public string ApplicantPhoneNumber { get; set; }
public string ApplicantEmail { get; set; }
[DataType(DataType.DateTime)]
[DisplayFormat(ApplyFormatInEditMode = true,DataFormatString = "{0:dd/MM/yyyy HH:mm}")]
public DateTime DateOfAppointment { get; set; }
public string ThemeColour { get; set; }
public Boolean Confirmed { get; set; }
[ForeignKey("Admins")]
public int AdministrationId { get; set; }
[ForeignKey("Church")]
public int ChurchId { get; set; }
//[ForeignKey("Volunteers")]
//public int VolunteerId { get; set; }
public virtual Church Church { get; set; }
public virtual Administration Admins { get; set; }
//public virtual Volunteer Volunteers { get; set; }
}

As far I understand from your question is that, you want a one to many relationship from Appointments to Volunteer.
Just put a ICollection navigation property of Volunteers in the Appointments model. Which will be considered as one to many relation. Like one Appointment can be eligible for multiple/many volunteers
class Volunteer
{
//properties
}
class Appointments
{
//other properties
//....
//. . .
//Navigation property/foreign key for Volunteers(one-many)1 appointments=many volunteers
ICollection<Volunteer> Volunteers {get;set;}
}

You mention a Ceremony which is later not mentioned again. I assume a Ceremony is what you call an Appointment elsewhere.
This is what I read from your classes, and what I think you have defined correctly.
You seem to have a collection of Churches. Every Church has zero or more Volunteers and every Volunteer belongs to exactly one Church. This is called a one-to-many relationship
Every Volunteer can have zero or more Appointments.
Unclear:
- does an Appointment belong to only one Volunteer, or can several Volunteers attend the same Appointment?
- Is the applicant a Volunteer? In that case: don't copy Volunteer properties to an Applicant
- Do all Volunteers that attend a meeting belong to the same Church, or can the attendants from a meeting belong to different Churches?
It seems to me more logical that people from different Churches might attend the same meeting. That means that every Volunteer can attend zero or more meetings (Appointments), and every meeting is attended by zero or more Volunteers, possibly from different Churches: this is a true many-to-many relationship.
Apparently there is also a relation between Churches and Appointments. It is unclear whether this is the Church who hosts the appointment, or whether this is the location of the Appointment. Anyway, let's assume that every Church hosts zero or more Appointments and every Appointment is held at exactly one Church
If you'd follow the entity framework code first conventions, the following would be enough:
class Church
{
public int Id {get; set;}
// every Church has zero or more Volunteers:
public virtual ICollection<Volunteer> Volunteers {get; set;}
// every Church hosts zero or more Appointments:
public virtual ICollection <Appointment> Appointments {get; set;}
... // other Church properties
}
class Volunteer
{
public int Id {get; set;}
// every Volunteer belongs to exactly one Church using foreign key
public int ChurchId {get; set;}
public virtual Church Church {get; set;}
// every Volunteer has zero or more Appointments:
public virtual ICollection<Appointment> Appointments {get; set;}
... // other Properties
}
class Appointment
{
public int Id {get; set;}
// every Appointment is attended by one or more Volunteers (many-to-many)
public virtual ICollection<Volunteer> Volunteers {get; set;}
// every Appointment is hosted by exactly one Church using foreign key
public int ChurchId {get; set;}
public virtual Church Church {get; set;}
... // other properties
}
class MyDbContext : DbContext
{
public DbSet<Church> Churches {get; set;}
public DbSet<Volunteer> Volunteers {get; set;}
public DbSet<Appointment> Appointments {get; set;}
}
Because I followed the entity framework conventions, this is all entity framework needs to know to understand that I planned a one-to-many between Church and Volunteers and a many-to-many between Volunteers and Appointments. Entity Framework will recognize the primary and foreign keys, and will even create the junction table for your many-to-many relationship. There is no need for attributes nor fluent API. Only if you want different table names or column names you'll need attributes or fluent api.
Usage:
Give me all Appointments for Volunteer "John" that are held in St Bartholomew Church:
var appointmentsofJohnInStBarth = dbContext.Appointments
.Where(appointment => Appointment.Volunteer.Name = "John"
&& appointment.Church.Name = "Saint Bartholomew")
.Select(appointment => new
{
StartTime = appointment.StartTime,
EndTime = appointment.EndTime,
Description = appointment.Description,
Attendants = appointment.Volunteers
.Select(volunteer => new
{
Name = volunteer.Name,
Function = volunteer.Function,
Email = volunteer.Email,
...
})
.ToList(),
});
Because your DbContext knows about the relations between the Volunteers / Churches / Appointments, the DbContext knows when to perform a GroupJoin for the one-to-many or a join on the JunctionTable for the many-to-many relationships, without you having to specify which joins must be performed.

Related

Entity Framework issues when I need to have multiple relations of the same type

This issue has been dogging me for a while, and would appreciate any help
Let's say I have two classes, users and room, that basically go like this
public class User
{
[Key]
public int UserId {get; set;}
public string Username {get; set;}
public virtual List<Room> Rooms {get; set;}
}
public class Room
{
[Key]
public int RoomId {get; set;}
[ForeignKey("Owner")] // have tried with and without the explicit relationship
public int OwnerId {get; set;}
public User Owner {get; set; }
public virtual List<User> Members {get; set;}
}
My hope is to build s structure that allows a user to have multiple rooms where they are the owner, but also be included into multiple rooms as a member.
The problem comes when I actually try to make a room and add the members. The owner properties are added, but the Members list is always empty.
I'm using the annotation API, but have also tried the fluent API
When I try this, I get a multiplicity error
modelBuilder.Entity<Room>()
.HasMany(t => t.Members).WithMany(u => u.Rooms);
I am sure the answer is simple, I am just not that good with entity. Any help would be appreciated.
Thanks
You have two relationships between User and Room:
Rooms where user is the owner (one-to-many)
Rooms where user is a member (many-to-many)
Single Rooms collection cannot represent the two relationships, you need two collections:
public class User
{
[Key]
public int UserId {get; set;}
public string Username {get; set;}
public virtual List<Room> OwnerOfRooms {get; set;}
public virtual List<Room> MemberOfRooms {get; set;}
}
and use the following Fluent configuration:
modelBuilder.Entity<User>()
.HasMany(u => u.OwnerOfRooms)
.WithRequired(r => r.Owner)
.HasForeignKey(r => r.OwnerId);
modelBuilder.Entity<User>()
.HasMany(u => u.MemberOfRooms)
.WithMany(r => r.Members);
Note that the second relationship will create automatic junction table UserRooms.
If you are using EF Core, to create a Many-to-Many, which is what you're after, you'll need to have another entity, UserRoom, which contains a 1-to-Many with Users and 1-to-Many with Room, like:
public class UserRoom
{
public int UserId { get; set; }
public User User { get; set; }
public int RoomId { get; set; }
public Room Room { get; set; }
}
Then in User:
[InverseProperty(nameof(UserRoom.User))]
public ICollection<UserRoom> UserRooms { get; set; }
and similarly in Room:
[InverseProperty(nameof(UserRoom.Room))]
public ICollection<UserRoom> UserRooms { get; set; }
In your case, it sounds like you want two many-to-many relationships - one for room membership and one for room ownership. In that case, you'll need to implement this pattern twice, with linking entities perhaps more appropriately named OwnerRoom and MemberRoom.
If room ownership is unique to a single user, then you'll need MemberRoom and the pattern above along with a standard 1-to-many relationship with Room and User, like you've already implemented, just decorate User.Rooms with:
[InverseProperty(nameof(Room.Owner))]

one to many and self referencing in entity framework

I have two entities with following relationShip (these entities are taken for example purpose only)
public class Entity
{
public long ID { get; set; }
}
public class Doctor : Entity
{
public string Name {get; set;}
public string sprcialization { get; set;}
public string Icollection<JrDoctor> childDoctors { get; set;}
}
public class JrDoctor : Entity
{
public long? DoctorId { get; set;}
public virtual Doctor Doctor { get; set;}
public long? JuniorDoctorId { get; set;}
[ForeignKey("JuniorDoctorId")]
public virtual Doctor JuniorDoctor { get; set;}
}
this relationship in entityframework is creating an extra column Doctor_Id in JrDoctor table. Why is it so? and how can I avoid it using data annotations.
Here is how EF works - if it sees navigation property (Doctor in your case), then EF understands that both entities are related to each other. Relation in database is defined by foreign keys. So EF generates foreign key with name PropertyName_KeyColumnOfRelatedEntity. That's why you see column Doctor_Id in JrDoctor table.
If you don't want default generated foreign key column, then you should tell EF what it should use instead. That is done via data annotations attributes or fluent configuration. I prefer latter one:
modelBuilder.Entity<JrDoctor>()
.HasOptional(jd => jd.Doctor)
.WithMany(d => d.childDoctors)
.HasForeignKey(jd => jd.DoctorId); // here you tell which column is FK
Data annotations require modification of entity classes. In your case you should add attribute which tells name of FK for navigation property, just as you did for JuniorDoctor:
public class JrDoctor : Entity
{
public long? DoctorId { get; set;}
[ForeignKey("DoctorId")]
public virtual Doctor Doctor { get; set;}
public long? JuniorDoctorId { get; set;}
[ForeignKey("JuniorDoctorId")]
public virtual Doctor JuniorDoctor { get; set;}
}
InverseProperty did the trick.
public class Entity
{
public long ID { get; set; }
}
public class Doctor : Entity
{
public string Name {get; set;}
public string sprcialization { get; set;}
[InverseProperty("Doctor")]
public string Icollection<JrDoctor> childDoctors { get; set;}
}
public class JrDoctor : Entity
{
public long? DoctorId { get; set;}
[ForeignKey("DoctorId")]
public virtual Doctor Doctor { get; set;}
public long? JuniorDoctorId { get; set;}
[ForeignKey("JuniorDoctorId")]
public virtual Doctor JuniorDoctor { get; set;}
}

EF 6 using TPT error both have the same primary key value

I have one big question about TPT + EF6.
At my DB model I have one table Person (basic information of persons in my application) and I have tables for Supplier and Consumer.
My classes are:
//to table dbo.Person
public class Person
{
public long Id {get; set;} //is pk
public string Name {get; set;}
}
//to table dbo.Supplier
public class Supplier : Person
{
public long Id {get; set;}//is pk and fk
public string ProductName {get; set;}
}
//to table dbo.Consumer
public class Consumer
{
public long Id {get; set;} //is pk and fk
public string budget {get; set;}
}
If I have one person that is both supplier and consumer, and I get the records from same/different DBContext or navigate form another Entitys, then EF throws an exception:
All objects in the EntitySet Person must have unique primary keys. However, an instance of type Supplierand an instance of type Consumer both have the same primary key value, EntitySet=Person;ID=20.
Is there a way to specify one discriminator in TPT inheritance?
How do I solve this issue?
I suggest the pattern you actually need is Table Per Concrete Class
This can be achieved by
public class Person
{
public int Id { get; set; }
public Supplier Supplier { get; set; }
public Consumer Consumer { get; set; }
}
public class Supplier
{
public int Id { get; set; }
public string ProductName { get; set; }
}
public class Consumer
{
public int Id { get; set; }
public string Budget { get; set; }
}
Remember to put the following in your dbcontext
public DbSet<Supplier> Suppliers { get; set; }
public DbSet<Consumer> Consumers { get; set; }
public DbSet<Person> People { get; set; }

Entity Frame work Unique field issue

I am using EF Model first to create two entities
public class Brief
{
public int Id { get; set; }
public string Title { get; set; }
public string tId {get; set;}
public int SpeakerId { get; set; }
}
public class Speaker
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
What I want to do is in Brief entity decorate tId field as Unique.
Second when I run entities as it is, it creates the database but it does not create foreigh key relation between SpeakerId in Briefs table and Speakers
Please let me know how
1. Decorate tId as unique
2. Why it is not creating the foreign key relation on SpeakerId and Speakers table?
Thanks
For problem number 2 you need to add a navigational property to your entity:
public class Brief
{
public int Id { get; set; }
public string Title { get; set; }
public string tId {get; set;}
public int SpeakerId { get; set; }
//Navigational property
public virtual Speaker Speaker { get; set;} 1 Brief has 1 Speaker
}
Depending on the Relationship this can also be public virtual ICollection<Speaker> Speakers { get; set;} Vice Versa for the Speaker entity:public virtual Brief Brief { get; set;} ` or the ICollection for n:m / 1:m relations
The unique constraint on non key columns should not be implemented as of yet based on http://entityframework.codeplex.com/workitem/299
Further reading / related questions:
Setting unique Constraint with fluent API?
http://bit.ly/OcE2HV
See Unique key with EF code first
Dependent on the EF version you can set an attribute on the property.
Use a navigational property so EF can determine the relation.
Note that the virtual keyword denotes lazy loading. See Entity Framework Code First Lazy Loading

C# EF Code first One To Many Models

I have a DB that looks like this:
Houses
- HouseId
Rooms
- RoomId
HouseRooms
- HouseRoomId
- HouseId
- RoomId
class House
{
[Key]
public virtual int HouseId{ get; set; }
public DbSet<HouseRoom> Rooms{ get; set; }
}
class HouseRoom
{
[Key]
public virtual int HouseRoomId{ get; set; }
public virtual int HouseId{ get; set; }
public virtual int RoomId{ get; set; }
[ForeignKey("RoomId")]
Public Role RoomInfo {get; set;}
}
class Room
{
[Key]
public virtual int RoomId {get; set;}
public string RoomName {get; set;}
}
I just need the House entity/mode to load all the rooms records. Noticed that the JOIN table has a PK name HouseRoomId but this is not the key that need to match the key in House. House.HouseId need to match HouseRoom.HouseId.
How can I get this to work?
Ok first this is a many-to-many, not one-to-many. Second, you need to set the relationship on ModelCreation to let the DbContext know about this relationship. Also, you need to make your List<Room> virtual to enable it to load when you need it.
Please look at this example.
Code First Entity Framework Many-to-Many relationships
Also, for the record, you do not need to specify [key], it is already there for you by default

Categories

Resources