EF CF how to create one table that has three foreign keys - entity-framework

I have in my model Student that have a collection of all his subjects and every subject have collection of Educational matches.
public class Subject
{
public int SubjectID { get; set; }
public string SubjectName {get; set; }
public ICollection<Student> { get; set; }
}
public class EducationalMatches
{
public int EducationalMatchesID { get; set; }
public int Number { get; set; }
public string Name { get; set; }
}
public class Student
{
public int StudentID { get; set; }
public Icollection<AllStudentSubjects> AllStudentSubjects{ get; set; }
}
public class AllStudentSubject
{
public int AllStudentSubjectID { get; set; }
public Subject Subject { get; set; }
public ICollection<EducationalMatches> Educations { get; set; }
}
I'm expecting that in DB a table that looks like that will appear:
tableID
StudentID
SubjectID
EducationMatchesID
but no such table appears.
anyone have an idea?

Having a model is not enough, you need to override OnModelCreating method (it is empty by default). Plus EF wants to have 'reverse' property, for example, if Student has a collection of Subjects, Subject should have a collection of Students (for many-to-many relationship)
In your case for AllStudentSubject it should be like this (did not test)
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<AllStudentSubject>()
.HasKey(a => a.AllStudentSubjectID ) //Primary key, I prefer [Key] attribute, but this also works
.HasRequired(a => a.Student) //Property in AllStudentSubject
.WithMany(s => s.AllStudentSubjects) // Collection property in Student class
.HasForeignKey(a => a.StudentId)//Second property in AllStudentSubject
//For Student, you do not have to write this all again, just the primary key
modelBuilder.Entity<Student>()
.HasKey(a => a.StudentId ) //I like to move 'simple' declarations like this to the top
}
For other two entities you have to do the same.
Here`s a great article with all concepts explained

Related

How to create grandparent foreign key in EFCORE

I have an employee, with one hourly paying job, each hourly has multiple timecards. I would like the timecards to link to both the employee and Hourly.
public class Employee
{
public int Id { get; set; }
}
public class Hourly
{
public int EmployeeId { get; set; }
public List<Timecard> Timecards{ get; set; }
}
public class Hourly
{
public int HourlyId{ get; set; }
public int EmployeeId { get; set; }
}
How do I specify this relationship in EF.
The code appears to set the employeeID but causes issues with the migration and the Hourly is now set to null.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Timecard>()
.HasOne<HourlyPC>()
.WithMany(pc => pc.Timecards)
.HasForeignKey(t => t.EmployeeId)
.HasPrincipalKey(pc => pc.EmployeeId);
}
Violates 3NF, meaning duplicated data that can lead to problems such as data anomalies. One hack would be to include the Employee FK in a composite PK for Job. That way, when a Timecard has a FK to Job, it also has a FK to Employee. Perhaps you could use a job code for a second field for inclusion in the composite Job PK or reference another entity, an example of which is below where Position is the normalized details of a Job w/o employee specific data (e.g. hourly rate) and Job relates an Employee to a Position with the employee-specific details:
public class Employee
{
public int Id { get; set; }
}
public class Job
{
public int EmployeeId { get; set; }
public Employee Employee { get; set; }
public string PositionId { get; set; }
public Position Position { get; set; }
public ICollection<TimeCard> TimeCards { get; set; }
public decimal HourlyRate { get; set; }
}
public class TimeCard
{
public Id { get; set; }
public int EmployeeId { get; set; }
public Employee Employee { get; set; }
public string PositionId { get; set; }
public Job Job { get; set; }
}
Config:
// configure Job
// configure relationshipt to Position
modelBuilder.Entity<Job>()
.HasOne(j => j.Position)
.WithMany()
.IsRequired();
// configure relationship to Employee
modelBuilder.Entity<Job>()
.HasOne(j => j.Employee)
.WithMany()
.IsRequired();
// create composite PK using the two FK's
modelBuilder.Entity<Job>()
.HasKey(j => new { j.EmployeeId, j.PositionId });
// configure TimeCard
// configure nav prop to Employee
modelBuilder.Entity<TimeCard>()
.HasOne(tc => tc.Employee);
// configure relationship with Job
modelBuilder.Entity<TimeCard>()
.HasOne(tc => tc.Job)
.WithMany(j => j.TimeCards)
.HasForeignKey(tc => new { tc.EmployeeId, tc.PositionId })
.IsRequired();
That might need a bit of tweaking but that's the nuts and bolts of it.

Base class as foreign key in Entity Framework Code First

Lets say I have some classes:
public class BaseModel
{
[Key]
public int Id { get; set; }
}
public class Person : BaseModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public string Email { get; set; }
}
public class Employee : Person
{
public string Position { get; set; }
public decimal Wage { get; set; }
public PaymentType PaymentType { get; set; }
public virtual Company Company { get; set; }
}
Currently I have this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Employee>().HasRequired(e => e.PaymentType);
modelBuilder.Entity<Employee>().Map(t =>
{
t.MapInheritedProperties();
t.ToTable("Employees");
});
modelBuilder.Entity<Company>().HasMany(c => c.Employees).WithRequired(e => e.Company).Map(t => t.MapKey("Company_Id"));
}
I get two tables for Person and Employee, but I don't like what MapInheritedProperties() does by adding the Person properties to the Employee table.
How do I make the base class(Person) a foreign key?
In order to use the base class as a foreing key / navigational property without primary key problems. You need to be using Table per Type or Table per Hierarchy.
In your case using that modelBuilder should do it.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Employee>().HasRequired(e => e.PaymentType);
modelBuilder.Entity<Person >().ToTable("Persons");
modelBuilder.Entity<Employee>().ToTable("Employees");
modelBuilder.Entity<Company>().HasMany(c => c.Employees).WithRequired(e => e.Company).Map(t => t.MapKey("Company_Id"));
}
With this two table will be created. On names Persons will all fields for a person and one "Employees" for all fields for an employee. Both table will share the same primary key
You can get a really detailed explaination on Mortenza Manavi's blog

MVC EF code first creating model class

I'm new to MVC and EF code first. I'm in struggle to model a real-estate company DB model using EF code-first approach and I did some exercises as well as reading some online tutorials.
First thing I have a customers table that would be in relation with one or more properties he/she has registered as it's owner to sell or to rent, I was wondering if it is possible to have some sub classes inside a model class for registered properties as below:
public Property
{
public int PropertyID { get; set; }
public bool IsforSale { get; set; }
public bool IsforRent { get; set; }
public class Apartment{
public int ApartmentID { get; set; }
public int AptSqureMeter { get; set; }
. . .
. . .
}
public class Villa{
public int VillaID { get; set; }
public int VillaSqureMeter { get; set; }
. . .
. . .
}
and also other sub-classes for other types of properties
}
If the answer is Yes, then how should I declare the relations using data annotation or Fluent API, and then please help me how to update both Customers table and Property table with the customer information and property info at the same time?
thanks for your answer in advance.
As #Esteban already provided you with a pretty detailed answer on how to design your POCOs and manage the relationship between them, I will only focus on that part of your question:
how should I declare the relations using data annotation or Fluent API
First of all, you should know that certain model configurations can only be done using the fluent API, here's a non exhaustive list:
The precision of a DateTime property
The precision and scale of numeric properties
A String or Binary property as fixed-length
A String property as non-unicode
The on-delete behavior of relationships
Advanced mapping strategies
That said, I'm not telling you to use Fluent API instead of Data Annotation :-)
As you seem to work on an MVC application, you should keep in mind that Data Annotation attributes will be understood and processed by both by Entity Framework and by MVC for validation purposes. But MVC won't understand the Fluent API configuration!
Both your Villa and Apartment classes have similar properties, if they are the same but as it's type, you could create an enum for that.
public enum PropertyType {
Apartment = 1,
Villa
}
public class Property {
public int PropertyID { get; set; }
public bool IsforSale { get; set; }
public bool IsforRent { get; set; }
public PropertyType PropertyType { get; set; }
public int SquareMeter { get; set; }
}
This way of modelating objects is refered as plain old clr object or POCO for short.
Assume this model:
public class User {
public int UserId { get; set; }
public string Username { get; set; }
public virtual List<Role> Roles { get; set; }
}
public class Role {
public int RoleId { get; set; }
public string Name { get; set; }
public virtual List<User> Users { get; set; }
}
Creating relations with fluent api:
Mapping many to many
On your OnModelCreating method (you'll get this virtual method when deriving from DbContext):
protected override void OnModelCreating(DbModelBuilder builder) {
// Map models/table
builder.Entity<User>().ToTable("Users");
builder.Entity<Role>().ToTable("Roles");
// Map properties/columns
builder.Entity<User>().Property(q => q.UserId).HasColumnName("UserId");
builder.Entity<User>().Property(q => q.Username).HasColumnName("Username");
builder.Entity<Role>().Property(q => q.RoleId).HasColumnName("RoleId");
builder.Entity<Role>().Property(q => q.Name).HasColumnName("Name");
// Map primary keys
builder.Entity<User>().HasKey(q => q.UserId);
builder.Entity<Role>().HasKey(q => q.RoleId);
// Map foreign keys/navigation properties
// in this case is a many to many relationship
modelBuilder.Entity<User>()
.HasMany(q => q.Roles)
.WithMany(q => q.Users)
.Map(
q => {
q.ToTable("UserRoles");
q.MapLeftKey("UserId");
q.MapRightKey("RoleId");
});
Mapping different types of relationships with fluent api:
One to zero or one:
Given this model:
public class MenuItem {
public int MenuItemId { get; set; }
public string Name { get; set; }
public int? ParentMenuItemId { get; set; }
public MenuItem ParentMenuItem { get; set; }
}
And you want to express this relationship, you could do this inside your OnModelCreating method:
builder.Entity<MenuItem>()
.HasOptional(q => q.ParentMenuItem)
.WithMany()
.HasForeignKey(q => q.ParentMenuItemId);
One to many
Given this model:
public class Country {
public int CountryId { get; set; }
public string Name { get; set; }
public virtual List<Province> Provinces { get; set; }
}
public class Province {
public int ProvinceId { get; set; }
public string Name { get; set; }
public int CountryId { get; set; }
public Country Country { get; set; }
}
You now might want to express this almost obvious relationship. You could to as follows:
builder.Entity<Province>()
.HasRequired(q => q.Country)
.WithMany(q => q.Provinces)
.HasForeignKey(q => q.CountryId);
Here are two useful links from MSDN for further info:
Configuring Relationships with the Fluent API.
Code First Relationships Fluent API.
EDIT:
I forgot to mention how to create a many to many relationship with additional properties, in this case EF will NOT handle the creation of the join table.
Given this model:
public class User {
public int UserId { get; set; }
public string Username { get; set; }
public virtual List<Role> Roles { get; set; }
pubilc virtual List<UserEmail> UserEmails { get; set; }
}
pubilc class Email {
public int EmailId { get; set; }
public string Address { get; set; }
public List<UserEmail> UserEmails { get; set; }
}
public class UserEmail {
public int UserId { get; set; }
public int EmailId { get; set; }
public bool IsPrimary { get; set; }
public User User { get; set; }
public Email Email { get; set; }
}
Now that we've added a new property into our join table ef will not handle this new table.
We can achieve this using the fluent api in this case:
builder.Entity<UserEmail>()
.HasKey( q => new {
q.UserId, q.EmailId
});
builder.Entity<UserEmail>()
.HasRequired(q => q.User)
.WithMany(q => q.UserEmails)
.HasForeignKey(q => q.EmailId);
builder.Entity<UserEmail>()
.HasRequired(q => q.Email)
.WithMany(q => q.UserEmails)
.HasForeignKey(q => q.UserId);

Defining foreign key constraints with Entity Framework code-first

I have following entity class called Code. It stores categories of different kinds - the data for which I would have otherwise needed to create many small tables e.g. User Categories, Expense Categories, Address types, User Types, file formats etc.
public class Code
{
public int Id { get; set; }
public string CodeType { get; set; }
public string CodeDescription { get; set; }
public virtual ICollection<Expense> Expenses { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
:
: // many more
}
The class Expense looks like this:
public class Expense
{
public int Id { get; set; }
public int CategoryId { get; set; }
public virtual Code Category { get; set; }
public int SourceId { get; set; }
public double Amount { get; set; }
public DateTime ExpenseDate { get; set; }
}
With the above class definitions, I have established 1:many relation between Code and Expense using the CategoryId mapping.
My problem is, I want to map the SourceId field in Expense to the Code object. Which means, Expense object would contain
public Code Source { get; set; }
If I use this, at runtime I get an error about cyclic dependencies.
Can someone please help?
You will need to disable cascading delete on at least one of the two relationships (or both). EF enables cascading delete by convention for both relationships because both are required since the foreign key properties are not nullable. But SQL Server doesn't accept multiple cascading delete paths onto the same table that are introduced by the two relationships. That's the reason for your exception.
You must override the convention with Fluent API:
public class Code
{
public int Id { get; set; }
//...
public virtual ICollection<Expense> Expenses { get; set; }
//...
}
public class Expense
{
public int Id { get; set; }
public int CategoryId { get; set; }
public virtual Code Category { get; set; }
public int SourceId { get; set; }
public virtual Code Source { get; set; }
//...
}
Mapping with Fluent API;
modelBuilder.Entity<Expense>()
.HasRequired(e => e.Category)
.WithMany(c => c.Expenses)
.HasForeignKey(e => e.CategoryId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Expense>()
.HasRequired(e => e.Source)
.WithMany()
.HasForeignKey(e => e.SourceId)
.WillCascadeOnDelete(false);

EF CF: complex types for legacy database

I successfully mapped my complex type like this:
modelBuilder
.ComplexType<Name>()
.Property(name => name.First)
.HasColumnName("firstNameColumn");
modelBuilder
.ComplexType<Name>()
.Property(name => name.Last)
.HasColumnName("lastNameColumn");
So far so good. But notice that we do not specify any entity type. What if we want to map the same complext type also for a table with columns "firstN" and "lastN"? I tried EntityTypeConfiguration<> but you are not allowed to specify complex types there. Finally it looks like that complexTypes are defined globally.
You can also customize the complex type columns names at the entity level, like the following:
public class User
{
public int UserId { get; set; }
public Name NameInfo { get; set; }
}
public class Customer
{
public int CustomerId { get; set; }
public Name NameInfo { get; set; }
}
[ComplexType]
public class Name
{
public string First { get; set; }
public string Last { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Name>()
.Property(name => name.First)
.HasColumnName("firstNameColumn");
modelBuilder.ComplexType<Name>()
.Property(name => name.Last)
.HasColumnName("lastNameColumn");
// Here is how can customize the column names at the entity level:
modelBuilder.Entity<Customer>().Property(u => u.NameInfo.First)
.HasColumnName("firstN");
modelBuilder.Entity<Customer>().Property(u => u.NameInfo.Last)
.HasColumnName("lastN");
}
}
And the resultant schema will be:
Here you can find another example.