Entity Framework - Multiple Foreign Keys Issue - c#

I have a Project model which has a ProjectLead (one instance of the Person Foreign Key), this works fine. But now I also need to add a collection of People (Project members) referencing the same Person table and I can't get the Entity Framework to generate my database. As soon as I try to add the Fluent API code to create the link table ProjectPerson I get an error - "Schema specified is not valid. Errors: The relationship 'MyApp.WebApi.Models.Person_Projects' was not loaded because the type 'MyApp.WebApi.Models.Person' is not available." I assume this is because of the existing FK relationship already in place with ProjectLead.
Project Model:
public class Project
{
[Key]
public int ProjectId { get; set; }
public string Name { get; set; }
// Foreign Key - Project lead (Person)
public int ProjectLeadId { get; set; }
public virtual Person ProjectLead { get; set; }
// Create many to many relationship with People - Team members on this project
public ICollection<Person> People { get; set; }
public Project()
{
People = new HashSet<Person>();
}
}
Person Model:
public class Person
{
[Key]
public int PersonId { get; set; }
public String Firstname { get; set; }
public String Surname { get; set; }
// Create many to many relationship
public ICollection<Project> Projects { get; set; }
public Person()
{
Projects = new HashSet<Project>();
}
}
DB Context:
public class HerculesWebApiContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Project> Projects { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// This works fine
modelBuilder.Entity<Project>()
.HasRequired(c => c.ProjectLead)
.WithMany(d => d.Projects)
.HasForeignKey(c => c.ProjectLeadId)
.WillCascadeOnDelete(false);
// Adding these lines to create the link table `PersonProjects` causes an error
//modelBuilder.Entity<Person>().HasMany(t => t.Projects).WithMany(t => t.People);
//modelBuilder.Entity<Project>().HasMany(t => t.People).WithMany(t => t.Projects);
}
}
I gather that perhaps I need to use the InverseProperty attribute, but I am not sure where this should go in this case?

Can you explicitly define your join table? So, define a ProjectPeople relationship and make the code something like this...
public class ProjectPerson{
[Key]
public int ProjectPersonId { get; set; }
[ForeignKey("Project")]
public int? ProjectId {get;set;}
public virtual Project {get;set;}
[ForeignKey("Person")]
public int? PersonId {get;set;}
public virtual Person {get;set;}
public string RelationshipType {get;set;}
}
Then your other 2 classes will look like this...
public class Project
{
[Key]
public int ProjectId { get; set; }
public string Name { get; set; }
// Foreign Key - Project lead (Person)
public int ProjectLeadId { get; set; }
public virtual Person ProjectLead { get; set; }
// Create many to many relationship with People - Team members on this project
public virtual ICollection<ProjectPerson> ProjectPeople { get; set; }
public Project()
{
ProjectPerson = new HashSet<ProjectPerson>();
}
}
And this..
Public class Person
{
[Key]
public int PersonId { get; set; }
public String Firstname { get; set; }
public String Surname { get; set; }
// Create many to many relationship
public virtual ICollection<ProjectPerson> ProjectPeople { get; set; }
public Person()
{
ProjectPerson = new HashSet<ProjectPerson>();
}
}

Related

Entity Framework: The Entity with schema was already defined

In my database, I have two tables, namely Book and User, that have a M-to-M relationship table named BookXUser. In the relationship table, there are two fields, which are the foreign keys of the Book and User, and another field named bookShelf. Since using only Entity Framework I couldn't find a way to insert a M-to-M relationship while inserting into the bookShelf at the same time, I decided to make a model for the relationship table, which are defined like this:
modelBuilder.Entity<Book>()
.HasMany<UserData>(s => s.user)
.WithMany(c => c.book)
.Map(cs =>
{
cs.MapLeftKey("bookID");
cs.MapRightKey("userID");
cs.ToTable("BookXUser");
});
modelBuilder.Entity<BookXUser>()
.HasRequired<UserData>(s => s.user)
.WithMany(g => g.bookXuser)
.HasForeignKey<int>(s => s.userID);
modelBuilder.Entity<BookXUser>()
.HasRequired<Book>(s => s.book)
.WithMany(g => g.bookXuser)
.HasForeignKey<int>(s => s.bookID);
The problems arrive when I execute the following line:
userDataRepo.FindBy(user => user.email == entry.email).FirstOrDefault()
The EntitySet 'BookUserData' with schema 'dbo' and table 'BookXUser' was already defined. Each EntitySet must refer to a unique schema and table.
How can I solve this error while keeping the relationship table as a model which references the BookXUser? The idea is that I am using lazy loading on the virtual methods found in the model of BookXUser so I can get my books and users more easily, and I also manually insert my data in it so I can populate the bookshelf field.
public class BookXUser : IEntityBase
{
public int ID { get; set; }
public int bookID { get; set; }
public virtual Book book { get; set; }
public int userID { get; set; }
public virtual UserData user { get; set; }
public string bookShelf { get; set; }
}
The Book entity
public class Book : IEntityBase
{
public int ID { get; set; }
public string title { get; set; }
public string isbn { get; set; }
public int noPage { get; set; }
public string edition { get; set; }
public string bLanguage { get; set; }
public byte[] bookPic { get; set; }
public string publisherSite { get; set; }
public string bookFormat { get; set; }
public DateTime releaseDate { get; set; }
public DateTime initialReleaseDate { get; set; }
public string publisher { get; set; }
public string overview { get; set; }
public virtual ICollection<Author> author { get; set; }
public virtual ICollection<Genre> genre { get; set; }
public virtual ICollection<UserData> user { get; set; }
public virtual ICollection<Rating> rating { get; set; }
public virtual ICollection<Review> review { get; set; }
public virtual ICollection<BookXUser> bookXuser { get; set; }
public Book()
{
this.author = new HashSet<Author>();
this.genre = new HashSet<Genre>();
this.user = new HashSet<UserData>();
this.rating = new HashSet<Rating>();
this.review = new HashSet<Review>();
this.bookXuser = new HashSet<BookXUser>();
}
}
The UserData enity
public class UserData : IEntityBase
{
public int ID { get; set; }
public string username { get; set; }
public string userpass { get; set; }
public string email { get; set; }
public byte[] userPic { get; set; }
public string userOverview { get; set; }
public DateTime joinedDate { get; set; }
public virtual ICollection<Book> book { get; set; }
public virtual ICollection<Review> review { get; set; }
public virtual ICollection<Rating> rating { get; set; }
public virtual ICollection<UserData> user { get; set; }
public virtual ICollection<BookXUser> bookXuser { get; set; }
public UserData()
{
this.book = new HashSet<Book>();
this.review = new HashSet<Review>();
this.rating = new HashSet<Rating>();
this.user = new HashSet<UserData>();
this.bookXuser = new HashSet<BookXUser>();
}
}
You defined your Book and UserData entities in a wrong way. As you say in your question you are trying to define a many-to-many relashionship between Book and UserData so you added a join entity BookXUser because that entity hold a data BookShelf. That join entity will use by convention BookXUser as a table name.
Also, you defined two collections:
ICollection<UserData> user in your Book entity
ICollection<Book> book in your UserData entity
You also use this fluent configuration in your OnModelCreating method:
modelBuilder.Entity<Book>()
.HasMany<UserData>(s => s.user)
.WithMany(c => c.book)
.Map(cs =>
{
cs.MapLeftKey("bookID");
cs.MapRightKey("userID");
cs.ToTable("BookXUser"); // <-- this is your error.
});
Doing it like this, you're adding another many-to-many relashionship between Book and UserData and use BookXUser as the name for the join table which is already used by your entity BookXUser you've created.
To solve your issue you don't need to add the collections and to configure many-to-many relationship fluently like you did. Why? Bacause you have an join entity BookXUser.
So :
remove ICollection<UserData> user from your Book entity and replace it with ICollection<BookXUser> BookXUser
remove ICollection<Book> book from your UserData entity and replace it with ICollection<BookXUser> BookXUser
remove the fluent configuration you added for many-to-many relashionship between UserData and Book.

c# ef - Best approach to create one-to-zero/one mapping between one common table to other three or more tables

I am trying to create a common database table of Contact for multiple types, like, Person, Company, Branch, etc using entity-framework 6.1 code first. I am stuck on trying to figure out the best way to implement the one-to-one mapping between a Contact and Person, Company, Branch where each of these table entries will have only one Contact.
I have four tables for this.
public class Contact
{
public Contact()
{
People = new HashSet<Person>();
Companies = new HashSet<Company>();
Branches = new HashSet<Branch>();
}
public int ContactId { get; set; }
public ICollection<Person> People { get; set; }
public ICollection<Company> Companies { get; set; }
public ICollection<Branch> Branches { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public int ContactId { get; set; }
public virtual Contact Contacts { get; set; }
}
public class Company
{
public Company()
{
Members = new HashSet<Member>();
}
public int CompanyId { get; set; }
public int ContactId { get; set; }
public virtual Contact Contacts { get; set; }
}
public class Branch
{
public int BranchId { get; set; }
public int ContactId { get; set; }
public virtual Contact Contacts { get; set; }
}
Problem: In the current implementation I could able to store the multiple contacts for each Person, Company and Branch i.e. Many-to-Many relationship between these table to Contact. Instead I would like to store the only one Contact for each Person, Company and Branch as naturally it should have only one.
I tried the below Implementation but it gives me error while retrieving the Contact information by PersonId
public class Contact
{
public Contact()
{
People = new HashSet<Person>();
}
public int ContactId { get; set; }
public virtual Person People { get; set; }
}
public class Person
{
public int PersonId { get; set; }
public virtual Contact Contacts { get; set; }
}
public class ContactMap : EntityTypeConfiguration<Contact>
{
public ContactMap()
{
// 1-1 relationships
HasOptional(p => p.People).WithOptionalPrincipal(c => c.Contacts).Map(c => c.MapKey("ContactId"));
}
}
Error while retrieving the Contact Info
_db.People.Join(_db.Contacts, p => p.ContactId, c => c.ContactId, (p, c) => new { p, c })
.Select(x => => new
{
x.p.PersonId, x.c.ContactId
})
.OrderBy(pid => pid.Id)
.ToList();
at p.ContactId it throws an error "Cannot resolve the symbol 'ContactId'" since there is no definition of ContactId in Person entity/class.
Any suggestions would be highly appreciated.
I would like to have an final output/database structure as below:
I have also experienced same problem. In my opinion the best approach here is to create one main table, say Party. Create one-to-one relationship between this table and your other main tables (Person, Company, Branch) and create one-to-zero or one relationship between main table (Party) and Contact table.
/// <summary>
/// Model for Party, which is general form of Persons and Companies
/// </summary>
public class Party
{
public int PartyID { get; set; }
// Navigation properties
public virtual Company Company { get; set; }
public virtual Person Person { get; set; }
public virtual Contact Contact { get; set; }
}
public class Person
{
public int PersonID { get; set; }
// Other properties.....
// Navigation properties
public virtual Party Party { get; set; }
}
public class Company
{
public int CompanyID { get; set; }
// Other properties
// Navigation properties
public virtual Party Party { get; set; }
}
public class Contact
{
public int ContactID { get; set; }
// Other properties...
// Navigation properties
public virtual Party Party { get; set; }
}
This link helped me to create my models.

One to many relationship, configure which field is referenced in Entity Framework

Here are the domains:
public class Person : Entity
{
public int Id { get; set; }
pucli ICollection<PersonChild> Children
}
public class PersonChild
{
public int Id { get; set; }
public int PersonId { get; set; }
public Person Person { get; set; }
public int ChildId { get; set; }
public Person Child { get; set; }
}
And here is my configuration:
HasRequired(p => p.Person).WithMany().HasForeignKey(pc => pc.PersonId);
My question is how can I configure the Children property of Person to reference on PersonIds in PersonChild? My current configuration keeps on referencing to ChildIds.
The solution to my problem is to specify which field is referenced in configuration:
HasRequired(pc => pc.Person).WithMany(p => p.Children).HasForeignKey(pc => pc.PersonId);

Fluent API EF 6 - How to configure 1:M (with M being of the same type)

I'm making my first steps with Fluent Api and I'm trying to understand how to do the following: This is my model.
public class Person
{
public int Id {get ; set;}
public List<View> Viewers {get; set;}
}
public class View
{
public int Id {get; set;}
public Person Viewer {get; set;}
}
This is a simplification of my model, I want to keep track of Person's that access some others profiles. I want to know for every person who has seen their profile.
I have tried this:
var view = modelBuilder.Entity<View>();
view.HasKey(v=>v.Id);
var person = modelBuilder.Entity<Person>();
person.HasKey(r => r.Id);
person.HasMany(t => t.Viewers).WithRequired(t=>t.Viewer);
I know this seems super silly, but from my code I want to be able to navigate:
Person -> Viewers (let's take viewer 1) -> Viewers, and so on...
Is this the right approach?
Thanks in advanced!
try this
public class Person
{ [Key]
public int Id { get; set; }
public ICollection<View> Viewers { get; set; }
}
public class View
{ [Key]
public int Id { get; set; }
public int ViewerId { get; set; } //this is a ForeingKey
public Person Viewer { get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<View> Views { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<View>().HasRequired(a => a.Viewer).WithMany().HasForeignKey(a => a.ViewerId);
base.OnModelCreating(modelBuilder);
}
}
you may do the same with DataAnnotation Attributes and then it does't need to use Fluent API.
public class View
{
public int Id { get; set; }
public int ViewerId { get; set; }
[ForeignKey("ViewerId")] // here is a foreignkey property
[InverseProperty("Viewers")] // here is a navigation property in Person class
public Person Viewer { get; set; }
}

Entity framework 5 many to many

I have problem because when I add the following to class Course I have only 2 tables not 3
public int PersonId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Student { get; set; }
you do not have these three lines all good, but I need an additional field in class Course
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Course> CoursesAttending { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; }
public int PersonId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Student { get; set; }
public virtual ICollection<Person> Students { get; set; }
}
public class SchoolContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Person> People { get; set; }
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer<SchoolContext>(
new DropCreateDatabaseAlways<SchoolContext>());
SchoolContext db = new SchoolContext();
var cos = from d in db.Courses
select d;
}
}
please help me
EF cannot decide if Course.Student or Course.Students refers to Person.CoursesAttending. You must give EF a hint, for example by using the [InverseProperty] attribute on Course.Students:
[InverseProperty("CoursesAttending")]
public virtual ICollection<Person> Students { get; set; }
Edit
The model will cause multiple cascading delete paths, namely: When you delete a Person records in the join table will be deleted as well, but it will also delete all Courses that this person is assigned to through the Course.Person property. The deleted Courses will delete records in the join table again which is the second delete path on the same table.
Multiple cascading delete paths are not allowed with SQL Server, so you must disable it with Fluent API:
public class MyContext : DbContext
{
//...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>()
.HasRequired(c => c.Student)
.WithMany()
.HasForeignKey(c => c.PersonId)
.WillCascadeOnDelete(false);
}
}

Categories

Resources