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"));
}
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 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?
I´am just in the beginning of creating a comment system for my website. I´am using EF and I want to bind a few of my tables to the Comments table. We can say that I have a Car entity and a Bike entity in two separate tables, and I would like to bind a collection of comments of these two tables.
In my mind I have a picture that the comments table would contain:
CommentID | EntityID | CommentText
1 Bike_2 Hello world..
2 Car_2 --
3 Bike_3 --
Am I thinking right? How do a setup this with entity framework?
Best regards.
(The following is for Entity Framework 4.1 to 4.3.1 and Code-First/DbContext.)
The type of mapping which comes closest to your idea is Table-per-Type (TPT) inheritance mapping. It would look like this:
public abstract class EntityWithComments
{
public int Id { get; set; }
public ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public string CommentText { get; set; }
public int EntityId { get; set; }
public EntityWithComments Entity { get; set; }
}
public class Car : EntityWithComments
{
public string Manufacturer { get; set; }
public string Color { get; set; }
}
public class Bicycle : EntityWithComments
{
public int Weight { get; set; }
public bool HasThreeWheels { get; set; }
}
EntityWithComments is a base class for Car and Bicycle and perhaps other entities. Then you have a derived DbContext class:
public class MyContext : DbContext
{
public DbSet<EntityWithComments> EntitiesWithComments { get; set; }
public DbSet<Comment> Comments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.ToTable("Cars");
modelBuilder.Entity<Bicycle>()
.ToTable("Bicycles");
}
}
As a result you have four tables in the database:
A Comments table which looks like your proposal but EntityId won't refer directly to the Cars and Bicycles tables. Instead it refers to the base type table EntitiesWithComments.
A table EntitiesWithComments representing the abstract base class and which only has a single column, namely the Id column.
A table Cars with a one-to-one shared primary key constraint between the Id and the Id in table EntitiesWithComments
A table Bicycles with a one-to-one shared primary key constraint between the Id and the Id in table EntitiesWithComments
You can then - for example - load all blue cars:
using (var ctx = new MyContext())
{
var blueCars = ctx.EntitiesWithComments.OfType<Car>()
.Where(c => c.Color == "Blue")
.ToList();
}
Because the EntitiesWithComments base table does not contain any column except the Id there is no join between the tables necessary. The generated SQL looks like this and only touches the table for the derived type:
SELECT
'0X0X' AS [C1],
[Extent1].[Id] AS [Id],
[Extent1].[Manufacturer] AS [Manufacturer],
[Extent1].[Color] AS [Color]
FROM [dbo].[Cars] AS [Extent1]
WHERE N'Blue' = [Extent1].[Color]
(I guess, the strange 0X0X value in this query is kind of a type descriptor EF uses to check if the returned rows are really cars, but I am not sure.)
If you want to load all bicycles with three wheels including their comments the following query works:
using (var ctx = new MyContext())
{
var bicyclesWithThreeWheelsWithComments = ctx.EntitiesWithComments
.Include(e => e.Comments)
.OfType<Bicycle>()
.Where(b => b.HasThreeWheels)
.ToList();
}
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.