I am using Code First appraoch in my MVC project. Just for simplicity I have got two classes Course and Modules where there is a many to many relationship between the entities. Here is the structure of my classes
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; }
public virtual ICollection<Module> Modules { get; set;
}
public class Module
{
public int ModuleId { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Course> Courses {get;set;
}
I created two controllers for each using EntityFramework where each controller creates the table for each class. Now having the many to many relationship the EF is smart enough to create the third junction table for me ie. CourseModule(FK_Course_ID, FK_ModuleID). But the problem is this table is not get populated with the keys from the respective tables. It is blank. Can anyone please help me how can I figure it out??
Thanks.
I am going out on a limb here but do you have the following in your
context class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Course>()
.HasMany(s => s.Modules)
.WithMany(a => a.Courses)
.Map(m =>
{
m.MapLeftKey("CourseID");
m.MapRightKey("ModuleID");
m.ToTable("CourseModules");
});
}
Also, can we see your code where you try to save Course / Module?
Related
I'm trying to wrap my head around a Many-to-Many relationship with Code-First mapping.
If I have an Album Class that can have many Genres (and vice-versa), I understand that I need to have an Intermediate table and Entity Framework will automatically do that for me. However, I would like a little more control over the Intermediate table, so I am creating one myself, the main reason is that I would like to be able to mark the row as deleted from the front-end and leave it in the database.
To do this for all my Classes I have created a BaseObject that they are Inherit from (I've removed many of the Annotations and other code to simplify this post):
public class BaseObject
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Oid { get; set;
public DateTime DateCreated { get; set; }
public bool IsDeleted { get; set; }
public DateTime? DeletedDate { get; set; }
}
After that we have the Albums and Genres Classes:
public class Album : BaseObject
{
public string Name { get; set; }
public virtual List<AlbumsGenres> Albums { get; set; }
}
public class Genre : BaseObject
{
public string Name { get; set; }
public virtual List<AlbumsGenres> Genres { get; set; }
}
Finally the AlbumsGenres Intermediate Class:
public class AlbumsGenres : BaseObject
{
// Left blank because EF will create "Album_Oid" and "Genre_Oid" columns
// Tried the below code, but EF still created it's own Columns
/*
public Guid Album { get; set; }
public Guid Genre { get; set; }
*/
}
The questions that I have; Is there a way to tell EF to create Album_Oid with a different Column Name like Album?
I would accept an answer of "Just don't worry about it", if a brief explanation (or link) was provided.
You can control the intermediate table, Normally I use explicit mapping but the following works with CodeFirst:
In Album, you want a List<Genre> (not AlbumGenre)
In Genre, you want a List<Album>
In your context, add the following override for OnModelCreating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Album>()
.HasMany(a => a.Genres)
.WithMany(g => g.Albums)
.Map(x =>
{
x.MapLeftKey("AlbumId");
x.MapRightKey("GenreId");
x.ToTable("AlbumGenres");
});
base.OnModelCreating(modelBuilder);
}
I have two models, One ApplicationUser which holds all users in the system and I have a Quotation model which will hold all Quotations made. now I want to store two mappings to ApplicationUser inside Quotations. So that I can map to created User as well as cancelled User. My model looks like this
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int ApplicationUserID { get; set; }
public virtual ApplicationUser CreatedUser { get; set; }
[ForeignKey("ApplicationUserID")]
public ApplicationUser CancelledUser { get; set; }
}
But this throws an error
Quotation_CancelledUser_Target_Quotation_CancelledUser_Source: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'ApplicationUserID' on entity 'Quotation' does not match the type of property 'Id' on entity 'ApplicationUser' in the referential constraint 'Quotation_CancelledUser'.
So I guess , The approach I am taking is wrong. Can anyone point out the correct way to achieve this?
The problem you are observing is called "Multiple Cascade Path". A Multiple Cascade Path happens when a cascade path goes from column col1 in table A to table B and also from column col2 in table A to table B.
The exception is caused by SQL Server when code first attempted to add table that has columns appearing more than once of another table.
In SQL Server, a table cannot appear more than one time in a list of all the cascading referential actions that are started by either a DELETE or an UPDATE statement. For example, the tree of cascading referential actions must only have one path to a particular table on the cascading referential actions tree.
You will need to use FluentAPI to configure the relationship. I am using EF5 currently and do not know if this can be accomplished in EF6/7.
So modifying your code sample, it would look like:
public class Quotation
{
public int QuotationID { get; set; }
public DateTime QuotationDate { get; set; }
public DateTime QuotationCancelDate { get; set; }
public int CreatedUserID { get; set; }
// Navigation property
public virtual ApplicationUser CreatedUser { get; set; }
public int CancelledUserID { get; set; }
// Navigation property
public virtual ApplicationUser CancelledUser { get; set; }
}
// Created a simple class for example
public class ApplicationUser
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
Now in you context class you can write:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Disable the default PluralizingTableNameConvention
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
// Add configuration here
modelBuilder.Entity<Quotation>()
.HasKey(e => e.QuotationID);
modelBuilder.Entity<ApplicationUser>()
.HasKey(e => e.Id);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CreatedUser)
.WithMany()
.HasForeignKey(u => u.CreatedUserID);
modelBuilder.Entity<Quotation>()
.HasRequired(a => a.CancelledUser)
.WithMany()
.HasForeignKey(u => u.CancelledUserID);
}
For more information with example refer this link.
I want an object to reference itself. How do I write this model? For eg.
public class Term
{
public int TermId { get; set; }
public string Name { get; set; }
public virtual Term PreviousTerm { get; set; }
public virtual int? PreviousTermId { get; set; }
}
The schema generated is:
TermId
Name
PreviousTermId
PreviousTerm_TermId
So apparently, PreviousTermId serves no purpose here as a relationship FK.
But when using automapper, I have to map to PreviousTermId, I cant create the new object PreviousTerm and assign the Id to that. How do I fix this?
Try specifying the mappings in onModel OnModelCreating event
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Term>().HasOptional(t =>t.PreviousTerm).WithMany().
HasForeignKey(t=>t.PreviousTermId);
}
I am trying to build a data model that I can use with Entity Framework 4.1.
I am tring to build a simple app to manage events (like a birthday party). So I figure I will have two types of users, Admins and Attenders. The admins will be able to create and manage the event and the attenders will only be able to view an event they are invited to.
I thought I only needed 2 classes for this but I am not sure. Here is waht I did for EF4.1
public class user
{
public int id { get; set; }
public string name { get; set; }
public ICollection<myevent> myadminevents { get; set; }
public ICollection<myevent> myinvites { get; set; }
}
public class myevent {
public int id { get; set; }
public string name { get; set; }
public ICollection<user> admin { get; set; }
public ICollection<user> attend { get; set; }
}
public class myeventcontext : DbContext
{
public DbSet<user> users { get; set; }
public DbSet<myevent> events { get; set; }
}
EF didnt do what I thought it would. It is ignoring my collections. So I don't think the model is right.
Any suggestions?
You can model this using two junction tables and configure the many-to-many relationships using fluent API
public class myeventcontext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Entity<User>()
.HasMany(user => user.myinvites).WithMany(event => event.attend)
.Map(m =>
{
m.ToTable("EventAttendees");
m.MapLeftKey("UserId");
m.MapRightKey("EventId");
});
Entity<User>()
.HasMany(user => user.myadminevents).WithMany(event => event.admin)
.Map(m =>
{
m.ToTable("EventAdmins");
m.MapLeftKey("UserId");
m.MapRightKey("EventId");
});
}
}
Other approach would be to have a single junction table with an additional column to store whether attendee is is an admin or not. But in this way you will not be able model many-to-many relationship without including the junction table as a class.
i have these models:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Books> FavoriteBooks { get; set; }
}
public class Books
{
public int Id { get; set; }
public string Title { get; set; }
public string Author{ get; set; }
}
When i run DBContext and check the generated tables, the Books table have created Student_Id column. I want the Books table to be just a reference and not linked back to Student.
How can I make this relationship uni-directional and will have no Student_Id column created in Books? can i use fluent-api? or in simple data annotations only?
thanks
In the fluent API it should look something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasMany(s => s.FavoriteBooks)
.WithMany();
}
That'll create a "StudentBooks" table for you to maintain the many-to-many relationship. It's not a one-to-many relationship as your question suggests, since a student can have many favourite books, but each book might be a favourite of many students.
Update
For the sake of completeness, here's how you'd do it if you already have that linking table defined and need to tell EF what the mapping looks like:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasMany(s => s.FavoriteBooks)
.WithMany();
.Map(cfg => cfg.MapLeftKey("Student_Id")
.MapRightKey("Books_Id")
.ToTable("StudentBooks"));
}