Code first mapping in entity framework - entity-framework

I have created 3 tables using Code First Approach. I get the following Model Validation Exception when i execute a Find on student table.
Student_courses_Target_Student_courses_Source: : The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
public class University
{
[Key]
public string Uni_ID { get; set; }
public virtual List<Course> Courses { get; set; }
}
public class Course
{
[Key]
[Column(Order = 1)]
public string Course_ID { get; set; }
[Key,ForeignKey("uni")]
[Column(Order = 2)]
public string Uni_ID { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual University uni { get; set; }
}
public class Student
{
[Key,ForeignKey("course"), Column(Order = 1)]
public string Course_ID { get; set; }
[ForeignKey("course"),Column(Order = 2)]
public string Uni_ID { get; set; }
[Key]
[Column(Order = 3)]
public string Student_ID { get; set; }
public virtual Course course { get; set; }
}
By my understanding , the exception means that i have not mapped my foreign keys in student table to the primary keys in course table. But i have done it . Is there an issue as to how the 'Uni_ID' occurs as Primary key in both University and Course Tables and perhaps i have gone wrong in referencing it as foreign key in the Student table ?

Related

Entity Framework and Introducing FOREIGN KEY constraint error in many-to-many relationship

I'm trying to create next model for a web app:
A company can create customers and categories (for customers).
A customer can belong to several categories.
Several customers can belong to the same category.
Entities:
public class Company
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Customer
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int CompanyId { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int CompanyId { get; set; }
[Required]
public string Name { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
}
Data context:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Company> Companies { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Customer> Customer { get; set; }
public ApplicationDbContext()
: base(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString, throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
When I run the app and try to get a company, I get the next error:
Introducing FOREIGN KEY constraint 'FK_dbo.CustomerCategories_dbo.Categories_Category_Id' on table 'CustomerCategories' 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.
I don't understand why I getting this error. I think it is a many-to-many relationship which is the problem. What am I doing wrong?

Entity Framework: field in table pointing to ID in same table (ParentID)

I've got the following model of my Request table:
public class Request
{
[Key]
[Column(Order = 0)]
public int Label_ID { get; set; }
[Key]
[Column(Order = 2)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public DateTime Date { get; set; }
public string Description { get; set; }
public string Memo { get; set; }
public DateTime DeadLine { get; set; }
**[ForeignKey("Label_ID, ID")]
[InverseProperty("Request")]
public int Parent_ID { get; set; }
public virtual Request Parent { get; set; }**
[ForeignKey("Label_ID")]
public virtual Label Label { get; set; }
[ForeignKey("RequestFrom")]
public int RequestFrom_UserID { get; set; }
public virtual ApplicationUser RequestFrom { get; set; }
[ForeignKey("RequestTo")]
public int RequestTo_UserID { get; set; }
public virtual ApplicationUser RequestTo { get; set; }
[ForeignKey("RequestAbout")]
public int? RequestAbout_UserID { get; set; }
public virtual ApplicationUser RequestAbout { get; set; }
public int? Project_ID { get; set; }
[ForeignKey("Label_ID, Project_ID")]
public virtual Project Project { get; set; }
}
}
The Parent_ID must be pointing to another Request by Label_ID and ID (composite key)
After creating a new add-migration I got the following error:
The ForeignKeyAttribute on property 'Parent_ID' on type 'iMaSys.Models.Request' is not valid. The navigation property 'Label_ID, ID' was not found on the dependent type 'iMaSys.Models.Request'. The Name value should be a valid navigation property name.
I know how to successfully point to other tables as you can see in RequestAbout_UserID. That migration was successfull. Only after adding Parent_ID I can't seem to solve this error.
Any help?
Best regards, Janno
You can use multiple ForeignKeyAttributes to refer to one parent, but you should also use the ColumnAttribute to indicate the column order of the key parts. But you've got an auto reference here, so there's a slight problem. The standard way to do this by data annotations would be:
public class Request
{
[Key]
[ForeignKey("Parent")]
[Column(Order = 0)]
public int Label_ID { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey("Parent")]
[Column(Order = 1)] // to refer to ID
public int Parent_ID { get; set; }
public virtual Request Parent { get; set; }
But this throws
The configured column orders for the table 'Requests' contains duplicates. Ensure the specified column order values are distinct.
You can fix this by changing a column index:
public class Request
{
[Key]
[ForeignKey("Parent")]
[Column(Order = 0)]
public int Label_ID { get; set; }
[Key]
[Column(Order = 2)] // Changed
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey("Parent")]
[Column(Order = 1)]
public int Parent_ID { get; set; }
public virtual Request Parent { get; set; }
And EF accepts it. The absolute column indexes don't matter, the relative order is what counts.
Still... I wouldn't like the somewhat obscure feature that column 1 refers to column 2. The code doesn't explain itself. I would prefer fluent mapping:
modelBuilder.Entity<Request>()
.HasRequired(p => p.Parent)
.WithMany()
.HasForeignKey(p => new { p.Label_ID, p.Parent_ID })

EF Code first : set optional one to one relationship with data annotation

I've the following situation I try to solve : I've 2 tables, a Course table with some fields and a CourseDescription table which is optional (so Course may have a CourseDescription but CourseDescription must have a Course). I'm trying to set this up. So far, here's what I have :
public class Course
{
[Key, Column("Key_Course")]
public int ID { get; set; }
public string Name { get; set; }
public virtual CourseDescription CourseDescription { get; set; }
}
public class CourseDescription
{
[Key, ForeignKey("Course")]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
public int CoursesID { get; set; }
[ForeignKey("CoursesID")]
public Course Course { get; set; }
}
This "works" meaning that EF doesn't complains about my model but the relation is not properly done because EF associate the PK of CourseDescription with the PK of Course. In my database, this is not the case (ex : CourseDescription.ID=1 is associated with CourseDescription.CoursesID=3, not 1).
Is there a way to fix that with data annotation ? I know I can use the fluent API but I don't want to override the model building just for that (unless there's no other way).
Thanks
Well, I think you have two choices:
Configure an one to many relationship
If you want to map the FK of the relationship between Course and CourseDescription, and you don't want to declare that FK property as Key of the CourseDescription entity, then, you don't have other choice that configure an one-to-many relationship. In that case your model would be like this:
public class Course
{
[Key, Column("Key_Course")]
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<CourseDescription> CourseDescriptions { get; set;}
}
public class CourseDescription
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
[ForeignKey("Course")]
public int CourseID { get; set; }
public Course Course { get; set; }
}
Configure an one-to-one relationship but not map the FK of the
relationship
The only way that EF lets you map the FK in an one-to-one relationship is when the FK is declared as a PK too, so if you want to have diferent Ids in both entities and you want to stablish an one-to-one relationship, then you could do something like this:
public class Course
{
[Key, Column("Key_Course")]
public int ID { get; set; }
public string Name { get; set; }
public CourseDescription CourseDescription { get; set;}
}
public class CourseDescription
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
[Required]
public Course Course { get; set; }
}
And work with the navigations properties.
It looks like you should not use ForeignKey attribute for ID property of CourseDescription class as you don't want to have an association between primary keys. Try to remove it.
Edit: It looks like I misunderstood the question previous time.
You can have your CourseDescription this way.
public class CourseDescription
{
[Key, ForeignKey("Course")]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
public Course Course { get; set; }
}
In this case you don't need to have CoursesID field. Entities will be connected by primary keys.

EntityFramework - Composite Key Table with FK to another Composite Key Table

I've two tables both with composite primary keys. Both have in common one of the primary key's with a foreign key to another table.
The problem is that when i create the migrations, it messes up the foreign keys.
I must use data annotations.
Example:
public class City
{
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string CityCode { get; set; }
[Key, Column(Order = 2)]
public string CompanyCode { get; set; }
public string Description { get; set; }
[ForeignKey("CompanyCode")]
public virtual Company Company { get; set; }
}
public class PostCode
{
[Key, Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Code { get; set; }
[Key, Column(Order = 1)]
public string CompanyCode { get; set; }
public string Description { get; set; }
public string CityCode { get; set; }
[ForeignKey("CompanyCode")]
public virtual Company Company { get; set; }
[ForeignKey("CityCode, CompanyCode")]
public virtual City City { get; set; }
}
PostCode and City have a composite primary key (Code, CompanyCode).
PostCode has a foreign key to the table City (CityCode, CompanyCode).
The problem is that the CompanyCode is part of the primary key and at the same time is part of the composite foreign key to City.
When i say that it messes up the foreign keys i mean the following:
CONSTRAINT [FK_dbo.PostCodes_dbo.Companies_CompanyCode] FOREIGN KEY ([CompanyCode]) REFERENCES [dbo].[Companies] ([CompanyCode]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.PostCodes_dbo.Cities_CompanyCode_CityCode] FOREIGN KEY ([CompanyCode], [CityCode]) REFERENCES [dbo].[Cities] ([CityCode], [CompanyCode])
In the second constraint, it references CompanyCode with CityCode and CityCode with CompanyCode.
I can't find any example in the internet with any scenario like this.
Where am i wrong?
Thanks in advance.
Edit 1
Between City and Company there is a simples primar key CompanyCode.
The same for PostCodes and Company.
If between City and Company you want to create a one-to-one relationship I'm afraid that is not possible following your model. When you are configuring a one-to-one relationship, Entity Framework requires that the primary key of the dependent end also be the foreign key, otherwise EF doesn't see it as one-to-one relationship. The dependend end in your case es City, but you have a problem, you want to add another PK, that is CityCode, that breaks what it means a one to one relationship because, for example, the below records could happen:
Company City
Id CityCode CompanyId
1 ee33a 1
2 aa23b 1
That's way, if you want to achieve your escenario, I guess that you have to create a one-to-many relationship between Company and City. Using Data Annotations could be this way:
public class City
{
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string CityCode { get; set; }
[Key, ForeignKey("Company"),Column(Order = 2)]
public string CompanyCode { get; set; }
public string Description { get; set; }
public virtual Company Company { get; set; }
}
public class Company
{
public string Id { get; set; }
public virtual ICollection<City> Cities { get; set; }
}
You can omit the Cities navigation property in Company if you don't want have reference to the cities related to a Company.
The same applies to the PostCode Entity.
Update:
To achieve what you want in the PostCode entity, you have to map the FKs this way:
public class PostCode
{
[Key, Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Code { get; set; }
[Key,ForeignKey("City"), Column(Order = 2)]
public string CityCompanyCode { get; set; }
public string Description { get; set; }
[ForeignKey("City"), Column(Order = 1)]
public string CityCode { get; set; }
public virtual City City { get; set; }
[ForeignKey("Company")]
public string CompanyCode { get; set; }
public virtual Company Company { get; set; }
}
Here is a good example of how you should treat the composite FKs
I always add a primary key to my tables, por example: CityId int, PostCode int. with this i resolve relations.
public class PostCode
{
[Key, Column(Order = 0)]
public string PostCodeId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Code { get; set; }
public string CompanyCode { get; set; }
public string Description { get; set; }
public string CityCode { get; set; }
[ForeignKey("CompanyCode")]
public virtual Company Company { get; set; }
[ForeignKey("CityCode, CompanyCode")]
public virtual City City { get; set; }
}
thanks

DbContext - Many to Many reliationship between three tables

Well i've been tring for some time to make Many-to-Many relationship between three tables.
Forum, Role, AccessMask.
In SQL It would like this:
ForumID | RoleID | AccessMaskID
--------------------------------
1 | 1 | 2
2 | 2 | 1
And so on. You got the idea. ForumID and RoleID is primary key for table. Question is.. how to do it in DbContext ?
It alawys screaming that entity is missing key. (really ?). I found a way to make and many-to-many relationship between two tables with modelBuilder, but as you see I still will be missing one more table
This will not be many-to-many relationship. You must expose this table as separate entity and map three one-to-many relationships. Many-to-many relationship works only between two tables if junction table contains only keys of these tables.
public class Forum
{
public int Id { get; set; }
...
public virtual ICollection<ForumRole> ForumRoles { get; set; }
}
public class Role
{
public int Id { get; set; }
...
public virtual ICollection<ForumRole> ForumRoles { get; set; }
}
public class AccessMask
{
public int Id { get; set; }
...
public virtual ICollection<ForumRole> ForumRoles { get; set; }
}
public class ForumRole
{
[Key, Column(Order = 0)]
public int ForumId { get; set; }
[Key, Column(Order = 1)]
public int RoleId { get; set; }
public int AccessMaskId { get; set; }
public virtual Forum Forum { get; set; }
public virtual Role Role { get; set; }
public virtual AccessMask AccssMask { get; set; }
}