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
Related
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.
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))]
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
How can i add a foreign key to a Model (code first)
i have a model Product which has an ID (primary key) which i want to add to my model order like.
public class Order
{
public int ID {get; set;}
[Required]
public int Total {get; set;}
[Required]
public int ProductId{get; set;}
}
but how can i make the ProductId refer to the id of my Product model like a foreign key?
On Stackoverflow there are alot simmilar questions but all with different answers, but they arent working for me. I really hope someone has a solution or can points me in the right direction with an explanation.
You need to add a navigation property:
public virtual Product Product { get; set; }
So the Order class will look like this:
public class Order
{
public int ID {get; set;}
[Required]
public int Total {get; set;}
[Required]
public int ProductId{get; set;}
[Required]
public virtual Product Product { get; set; }
}
Also, strictly speaking the ProductId property isn't needed once you have the virtual Product property.
You could do something like this:
public class Order
{
public int ID {get; set;}
[Required]
public int Total {get; set;}
[Required]
public int ProductId{get; set;}
[ForeignKey("ProductId")]
public virtual Product Product {get; set;}
}
I want the following table structure:
Person
-person_id
Company
-company_id
Company_Person
-person_id
-company_id
-other_column
Location
-id
Currently my EF is resulting in a 'company_id' column in the Person table also as a FK.
My models look like:
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PersonId { get; set; }
public int LocationId {get;set;}
public virtual Location Location {get; set; }
public virtual ICollection<CompanyPerson> CompanyPersons {get; set;}
}
public class Company
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CompanyId { get; set; }
public virtual ICollection<CompanyPerson> CompanyPersons {get; set;}
}
[Table("Company_Person")]
public class CompanyPerson
{
[Key, Column(Order = 0)]
public int PersonId { get; set; }
[Key, Column(Order = 1)]
public int CompanyId { get; set; }
public bool IsActive { get; set; }
public virtual Person Person { get; set; }
public virtual Company Company { get; set; }
}
public class Location
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id {get;set;}
public virtual ICollection<Person> Persons {get;set;}
}
I followed the same pattern as in here: Create code first, many to many, with additional fields in association table
How can I get that extra CompanyId column from being generated in the Person table?
Update
Ok I figured it out, and it turns out it was another association that I didn't post (my bad once again).
In my Company model I had this which I commented out and it generated the correct table. I still want this association so can someone tell me what is why this is happening?
public class Company
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CompanyId { get; set; }
public virtual ICollection<CompanyPerson> CompanyPersons {get; set;}
public virtual ICollection<History> Histories {get; set; }
}
public class History
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("Company")]
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
}
So when I commented out the everything in the History model except for the Id property, and the Company.History property it generated the table structure I was expecting.
I think that EF is treating your CompanyPerson property as a complex type, because essentially, it can't actually create a M2M relationship with what you've given it. Whether that's actually the problem or not, either way you'll need to fix your CompanyPerson properties to be:
public virtual ICollection<CompanyPerson> CompanyPersons { get; set; }
UPDATE
The oddest part is that your History class would perfectly explain the issue if it only actually was defined as:
public class History : Person
That's why I asked you about any subclasses of Person because EF's default behavior with inheritance is to use TPH (table per hierarchy). In other words, it will simply add all properties of all subclasses to the base class' table, instead of creating a table for each subclass. Plainly and simply, the only source of this column you aren't expecting is going to be one of either:
Company, or some subclass of Company has direct relationship to Person (not through CompanyPerson) and it's configured to be a one-to-one.
Person or some subclass of Person has a relationship to Company.
Ok I found the problem, and surprise surprise the real bug was with me!
In my Company table had this:
public virtual ICollection<Person> Histories { get; set; }
if you didn't catch that, the type should be History and not Person!
public virtual ICollection<History> Histories { get; set; }
Thanks for all that helped with this!